<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>马全一/马道长</title>
        <link>https://maquanyi.com</link>
        <description>Your blog description</description>
        <lastBuildDate>Tue, 17 Mar 2026 14:57:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>马全一/马道长</title>
            <url>https://maquanyi.com/favicon.ico</url>
            <link>https://maquanyi.com</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[100% user-supported]]></title>
            <link>https://maquanyi.com/articles/100-percent-user-supported</link>
            <guid>https://maquanyi.com/articles/100-percent-user-supported</guid>
            <pubDate>Sun, 18 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<blockquote>
<p>Why Obsidian is 100% user-supported and not backed by venture capital investors:</p>
<ol>
<li>We want to stay small, we don’t need to hire lots of people</li>
<li>We follow strict principles that we do not want to compromise</li>
<li>Our users are happy to support us, we don’t need VC money</li>
</ol>
</blockquote>
<p>概括上面的内容就是精英团队、坚持产品原则和从用户获取支持不融资。 我觉得理想状态下这些都是对的，就像每个姑娘都有个开咖啡屋或书店的梦想、每个男孩都有个开酒吧活着台球厅的理念一样。但现实之下能坚持做到每一点都不容易，坚持的路上那些困难可能我们都没有机会去面对，这算是一种另类的幸存者偏差吧。</p>
<p>一直觉得坚持原则是一个优秀产品经理必备的素质。在现实中会对自己说我们是在权衡利弊，不得不做一些妥协。我觉得核心问题其实不是这样，关键点是我们都是自私的，不愿意为自己的决策承担失败带来的责任，造成我们不敢坚持自己认为的原则；当然另一个方面，公司的体系内可能没有给你做出错误的决定的机会，大家不是追求 100 分或者 90 分，75 分可能是对所有人都有利的选择。所以，在国内产品经理做的事情大多数情况叫做产品化，就是给一个后端服务做界面的设计...</p>
<blockquote>
<p>Obsidian will not exist forever, no app will. However, the files you create in Obsidian are yours, and can hopefully last for generations. VCware is built with a five year horizon, it is not built to live on for decades.</p>
</blockquote>
<p>到底是谁的生命周期更长，是公司、产品还是 VC ？ 我觉得这段和题目之间的关系感觉有点远，应该是为下一段做铺垫。</p>
<blockquote>
<p>Many startup founders raise VC money because they need the upfront capital to build their product, or they see it as a shortcut to growth. For some products the capital truly is necessary, but too often it’s fueled by impatience and the inertia of Silicon Valley.</p>
</blockquote>
<p>为了短期快速增长而融资我觉得是没有问题的，有些市场是需要快速占领的。kenpano 的另一个观点是如果你选择市场够大，你就算融到钱也不可能快速占领整个市场。但是现在 OpenAI 的表现又恰好相反，到底什么是对的呢？</p>
<blockquote>
<p>In the short term, VCware tends to subsidize pricing to acquire users. It’s easier to grow if your product is cheap or free. But this generally comes at the cost of hoarding user data, and locking in customers. Once you’re in you can’t get out.</p>
</blockquote>
<p>所有产品都想锁定用户，只要是手段光明磊落，合理合法，我都是佩服的...</p>
<blockquote>
<p>To keep raising money, VCware startups must paint an increasingly enormous vision of their future, which becomes impossible to live up to. This leads to increasingly disparate priorities that gradually make the product worse. What starts off as a useful app becomes burdened with crap — a process Cory Doctorow has called enshittification.
Eventually all VCware must exit. That means being acquired or going public to pay back investors. It’s expected that 9 out 10 startups will fail. That’s just part of the math in a VC portfolio. The startups that have big exits pay for the ones that fail. Venture capital creates the unavoidable pressure to go big or go broke.</p>
</blockquote>
<p>资本都是逐利的，不能说资本都是恶的，拿钱都是有代价的，要在自己的能力范围内承担这个代价。最不能接受是被骗签了对赌，落的个倾家荡产，这样的投资人应该直接被打入地狱，那里有更合适他的工作。</p>
<blockquote>
<p>It is now possible for tiny teams to make principled software that millions of people use, unburdened by investors. Principled apps that put people in control of their data, their privacy, their wellbeing. These principles can be irrevocably built into the architecture of the app.
Principled people have always been able to make principled software. The difference is that now you need far less money and far fewer employees to reach far more customers. That wave is only just beginning.</p>
</blockquote>
<p>要有自己的产品原则，要坚持原则，要明白产品提现在原则上的界限...</p>
<blockquote>
<p>If you have principles and enough patience, being 100% user-supported is by far the most fun way to build.</p>
</blockquote>
<p>我是一个有原则的人，只要我有足够的钱，我就有足够的耐心，所以我去哪里有足够的钱呢？</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[2023 年回顾]]></title>
            <link>https://maquanyi.com/articles/2023-review</link>
            <guid>https://maquanyi.com/articles/2023-review</guid>
            <pubDate>Sat, 23 Dec 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>查看记录上一篇文章还是 10 月份在布鲁塞尔参加 EuroRust 2023 第一天的记录， 会议第二天结束是一个晚宴， 有几个餐车在露台上为大家提供肉丸、Taco 之类的食物。 翻了翻手机，也没有什么照片。记得当天和几个华为的同事聊了蛮多， 一位意大利的小哥当时休假要去阿曼， 把在场的几个人都惊掉了下巴。</p>
<p>然后在布鲁塞尔又待了几天， 记得当时准备去看瑞典队和比利时队的欧洲杯预选赛， 后来又懒得出门没有去， 当晚因为枪击事件也取消了比赛， 也打消了在欧洲去看一场球赛的念头。而后飞到了慕尼黑， 一直准备 Bazelcon 的演讲。 后来一直没有更新的原因也就是因为要准备这个演讲和 Demo ， 心理压力很大所以没有心情写任何的东西。</p>
<p>参加 Bazelcon 并且用英文演讲确实是今年一个值得纪念的事情。 首先今年投递了不少会议的 Topic ，但是只有这一个被接受了。 可能也只有 Google 比较重视 Monorepo ， 所以这个议题能够引起他们的注意。 如果继续沿着 Monorepo 这个路径发展， <a href="https://github.com/web3infra-foundation/mega">mega</a> 得到关注可能会比较少，现在从整体的策略上猜测， Google 很难在 AI 之外去推动什么。</p>
<p>这次欧洲之行的一个收获是说英语的信心。 之前对于自己这种野路子的英语和计算机其实心理一直都存在一些障碍，但是这次在欧洲演讲和交流好了很多，可能是年龄大了脸皮也厚了很多， 交流和演讲的时候主打一个 “ 只要我不尴尬，尴尬的就是老外 ”， 心理也就坦然了很多。当然这次演讲的时候还是很紧张， 到最后手抖敲错了几个字母， Demo 的一部分失败了。但是还能现场说两句引起大家笑场的话，感觉上跨过了讲英语的这个坎。</p>
<p>今年最大的收货还是 <a href="https://github.com/web3infra-foundation/mega">mega</a> 今年取得了非常大的进展， 不仅验证了可行性， 还基本完成了功能。 在 Mac M1 笔记本上能实现存储 3000 多万个文件的单一代码仓库存储， 总共使用了 80G 的 PostgreSQL 数据库空间。 对于普通 Git 仓库实现这么大的存储是基本不可能的。 当然今年没有大肆 <a href="https://github.com/web3infra-foundation/mega">mega</a> 进行公开宣传， 对于 <a href="https://github.com/web3infra-foundation/mega">mega</a> 的技术探索、方案和目标相对保留。 获取真实用户的反馈是下一阶段发展的重点，  2024 要通过宣传找到种子用户才能让 <a href="https://github.com/web3infra-foundation/mega">mega</a> 获得更多的机会。</p>
<p>当然 <a href="https://github.com/web3infra-foundation/mega">mega</a> 还是存在两个问题，争取在春节前解决掉，正式发布 0.1.0 版本为这一年的努力画上一个圆满的句号。 当前对 Pack 文件的解析还是存在性能问题， 由于处理 Git Pack 文件时不能像 Git 那样生成索引， 而是要直接直接解析出对象进行保存，造成了需要消耗内存来解决 Delta 对象的问题，当前版本还没有控制好内存的使用， 造成解析大一些仓库的 Pack 文件时内存占用过大，引起系统的 OOM ，之前测试都还顺利的原因是笔记本的内存都有 64G 没有意识到这个问题。 要不是 12 月份病了两次，现在可能已经有些进展了， 这个是版本发布前最需要解决的问题。</p>
<p>另外一个需要解决的问题是今年在 P2P 上实现的功能没有进行完整的测试， 而且对于 P2P 目前有一个场景是有明确需求的，就是为 AI 的基础数据或者模型提供版本管理工具。当前 Git LFS 在这个方向上实现的并不好， 大多数是把压力传递给 CDN 或者对象存储服务， 有非常大的成本屏障， 而且对于个人开发者使用在体验上也存在瓶颈。 希望这部分能在 0.1.0 版本前有实质的进展， 希望成为获取用户的关键特性。</p>
<p>今年在 <a href="https://github.com/Tongsuo-Project/RustyVault">RustyVault</a> 的支持下，在 Git 客户端实现了一些进展， 但是也看到了很多问题。 如果 <a href="https://github.com/web3infra-foundation/mega">mega</a> 继续发展， 在不重写 Git 的情况下，只能通过 Filter 来现实一些功能， 降低后续在服务端实现更多功能的难度， 也许终究会走向实现一个独立的 Git 路上， 从产品的角度上这不是一个好的选择， 先尽力用 Fitler 去做。</p>
<p>2023 年其实还启动了 <a href="https://github.com/rkos-dev">rkos</a> 和 <a href="https://github.com/rk8s-dev">rk8s</a> 两个使用 Rust 的技术探索项目， 有一些进展但是还没有实现既定的目标， 需要在 2024 年花费更多的精力把项目向前推动。  今年还计划借 <a href="https://github.com/web3infra-foundation/mega">mega</a> 项目， 写一个 Git 底层原理到上层逻辑的书籍，但是也没有更加实质的进展， 叠加在一起 2024 年要做的事情还有更多。</p>
<p>不管是收获还是遗憾， 2023 年都将要过去， 对比 2022 、 2021 等前几年至少取得了不错的收获。 总结下来，这几年的工作都是在一些开源项目的运营工作， 一个是外界对自己的印象可能不是那么技术， 另一个方面是这些年运作的项目都是其他人主导的， 虽然自己有很多技术和产品的思路， 但是受限于位置很难去实现想法。 <a href="https://github.com/web3infra-foundation/mega">mega</a> 是一个完全按照自己思路去实现的开源项目， 对它的期望也更高更加在意， 期望更加努力的去推动它的发展。 当然 2024 年 4 月是我在华为二进宫后第一个合同期的结束， 当人到中年的时候， 不禁会思考公司是不是给我续签合同。 如果是再年轻 10 年， 可能自己都不会回到华为待这么久。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[2023 年 Rust 生态发展回顾]]></title>
            <link>https://maquanyi.com/articles/2023-rust-review</link>
            <guid>https://maquanyi.com/articles/2023-rust-review</guid>
            <pubDate>Thu, 18 Jan 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>对 2023 年 Rust 编程语言的发展进行回顾，第一个被提起来的事情一定是 RustConf 2023 Keynote 事件。 <a href="https://thephd.dev/about/">JeanHeyd Meneide</a> 在网站上发布一篇文章 《<a href="https://thephd.dev/i-am-no-longer-speaking-at-rustconf-2023">I Am No Longer Speaking at RustConf 2023</a>》  正式拒绝参加 RustConf 2023 并且不再演讲，在社区中激起千层浪花。 当然 Rust Foundation 也第一时间在官方的 Blog 上对事件做出了<a href="https://blog.rust-lang.org/2023/05/29/RustConf.html">回应</a>，不管事情谁对谁错，我们可以透过整个事件看到当前整个 Rust 生态的状况。 Rust 编程语言依旧是由社区驱动的方式继续砥砺前行， 虽然有不同意见和争吵，同时保持着发展的强大动力和潜力，Rust 连续 8 年成为开发者最受<a href="https://survey.stackoverflow.co/2023/#section-admired-and-desired-programming-scripting-and-markup-languages">开发者喜爱的编程语言</a>。 Rust Foundation 和 Rust 社区依旧紧密合作，通过各种方式支持推动 Rust 生态继续扩大，被更多的开发者使用。</p>
<img alt="RustConf 2023 Keynote 事件" loading="lazy" width="887" height="175" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Faman.3fec82d0.png&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Faman.3fec82d0.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Faman.3fec82d0.png&amp;w=1920&amp;q=75">
<p>在 2022 年<a href="https://www.zdnet.com/article/linus-torvalds-rust-will-go-into-linux-6-1">Linus 接受 Rust 作为 Linux Kernel 的第二个编程语言</a>后，社区里面褒贬不一，持各种意见的人也在观望如何把 Rust 编写的内核模块运用到生产上。 当 2023 年 12 月 Google 提交了 <a href="https://lwn.net/Articles/953540/">Android Binder</a> 进入了内核，越来越多的开发者相信 Linus 的决定是正确的。 Android 13 新增 Native 代码中 <a href="https://security.googleblog.com/2022/12/memory-safe-languages-in-android-13.html">Rust 的比例已经接近 20%</a> ，在 Android 14 中预计接近 25% ，Google 不仅在 Android 中大量采用内存安全的 Rust 编程语言，也在 <a href="https://security.googleblog.com/2023/01/supporting-use-of-rust-in-chromium.html">Chromium 中也在引入 Rust </a>，让浏览器变得更加安全。</p>
<img alt="Linus 接受 Rust 作为 Linux Kernel 的第二个编程语言" loading="lazy" width="852" height="519" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FTorvalds.47c09283.jpg&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FTorvalds.47c09283.jpg&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FTorvalds.47c09283.jpg&amp;w=1920&amp;q=75">
<p>Microsoft 不仅提供了 <a href="https://github.com/microsoft/windows-rs">windows-rs</a> 帮助开发者使用 Rust 开发 Windows 的程序，更是在 Windows 11 Insider Preview Build 25905 版本中发布了 <a href="https://blogs.windows.com/windows-insider/2023/07/12/announcing-windows-11-insider-preview-build-25905">Rust 编写的 Windows 内核</a>。 当然这不是完全替代，而是部分用 Rust 进行开发，其中包含了一个 <a href="https://learn.microsoft.com/zh-cn/windows/win32/gdi/windows-gdi">GDI</a> 引擎的实现。 相信未来 Microsoft 会越来越多的在 Windows 上使用 Rust ，打造一个更加安全的操作系统。</p>
<img alt="Rust 编写的 Windows 内核" loading="lazy" width="593" height="504" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FMicrosoftNews-Windows11-rust-language-in-WIP-01.4028b89c.webp&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FMicrosoftNews-Windows11-rust-language-in-WIP-01.4028b89c.webp&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FMicrosoftNews-Windows11-rust-language-in-WIP-01.4028b89c.webp&amp;w=1200&amp;q=75">
<p>从 Rust 编程语言本身的发展来看，2023 年应该关注的两个变化。第一个是 Rust <a href="https://blog.rust-lang.org/2023/11/09/parallel-rustc.html">Nightly 版本中引入了并行编译的技术</a>，开始着手解决编译速度的问题，这标志着 Rust 编程语言更多进入到生产环境进行大型项目的开发，编译速度已经成为影响开发效率的关键问题。另一个是 <a href="https://blog.rust-lang.org/inside-rust/2023/11/15/spec-vision.html">Language Team 下成立了 Specification Team</a>， 由 Mara Bos 和 Felix Klock 任 Team Leader，开始了 Rust 编程语言规范的编写工作。 这是一个标志着 Rust 逐渐成熟的里程碑事件，且不提一个规范对于语言本身的重要性，它对于商业发展也是起到了至关重要的作用。 2023 年 11 月， 德国公司 Ferrous Systems 宣布使用了 Rust 编译器的一个子集通过了 ISO 26262 和 IEC 61508 的认证，打开了 Rust 编程语言进入汽车和工业自动化领域的大门。可预见 Rust 社区官方的 Specification 将大力推动 Rust 进入更多的商业领域，为 Rust 的大规模应用铺平道路。</p>
<img alt="Ferrous Systems" loading="lazy" width="800" height="200" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F1695823883485.e0100568.jpg&amp;w=828&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2F1695823883485.e0100568.jpg&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F1695823883485.e0100568.jpg&amp;w=1920&amp;q=75">
<p>为了适应 Rust 的快速发展，Rust 社区也在自己的组织架构上进行调整， <a href="https://blog.rust-lang.org/2023/06/20/introducing-leadership-council.html">Core Team 被 Leadership Council 取代</a>。 Leadership Council 将从 Top-level 的角度协调整个社区的工作，同时今年由 Leadership Council 选举了 5 名社区专家进入到了 Rust Foundation 的董事会，他们代表社区和董事会一起工作。 Rust 基金会引入了<a href="https://foundation.rust-lang.org/news/inside-the-rust-foundation-s-associate-membership-tier/">新的会员类型 Associate Membership Tier</a>， 非盈利组织、高校和科研机构可以作为成员加入到基金会，社区成员的多样性对社区发展是至关重要的。</p>
<blockquote>
<p><em>“In addition to creating formal collaborative processes to serve our peers in the nonprofit and education sectors, the Associate membership category allows the Rust Foundation to acknowledge different types of institutions that share our mission of supporting, growing, and providing resources to the Rust programming language ecosystem.”</em></p>
</blockquote>
<p>2023 年 11 月，对于任何一个编程语言都至关重要的厂商 <a href="https://foundation.rust-lang.org/news/member-spotlight-jetbrains/">JetBrains 加入 Rust Foundation</a> 成为 Sliver Member 。 不仅仅是加入， JetBrains 还带来了编写 Rust 编程语言的 IDE <a href="https://www.jetbrains.com/rust">Rust Rover</a> 。 同时 JetBrains 的下一代编辑器 <a href="https://www.jetbrains.com/fleet">Fleet</a> 也是使用 Rust 编程语言开发的。</p>
<img alt="JetBrains 还带来了编写 Rust 编程语言的 IDE - Rust Rover" loading="lazy" width="2560" height="1440" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frustrover_preview.be2113df.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frustrover_preview.be2113df.png&amp;w=3840&amp;q=75">
<p>这些只是我们看到 Rust 生态 2023 年发展的沧海一粟， Rust 社区正在以惊人的速度发展， Crates.io 第三方库在 2023 年突破 500 亿下载次数。 为了让更多的人学习和使用 Rust ，Rust Foundation 在 11 月启动了 <a href="https://foundation.rust-lang.org/news/the-rust-foundation-to-develop-training-and-certification-program">Developer Training 和 Certification Program</a> ，帮助开发者跨过学习曲线成为合格的 Rust 开发者。 对比 Rust Foundation 成立之前的生态发展轨迹，明显已经进入了快车道。 在 2024 年，可以期待 Rust 生态系统会继续蓬勃发展， 随着企业和开源社区对 Rust 的持续兴趣和投资，Rust 应用领域可能会进一步扩大，覆盖更多技术领域，吸引更广泛的开发者群体，期待 Rust 重塑软件生态。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[2024 年第一场 Rust Meetup 后记]]></title>
            <link>https://maquanyi.com/articles/20240106-rust-meetup</link>
            <guid>https://maquanyi.com/articles/20240106-rust-meetup</guid>
            <pubDate>Sat, 06 Jan 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>这次 Meetup 不是我组织的，是 Rustcc 和其它社区 Rust 爱好者组织的活动，这次我作为讲师分享 <a href="https://github.com/web3infra-foundation/mega">Mega</a> 的内容， 这也是我今年逐渐扩大宣传的一部分。</p>
<img alt="2024 年第一场 Rust Meetup" loading="lazy" width="1200" height="900" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FGDJCmXKa4AAat9B.a76cdc92.jpeg&amp;w=1200&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FGDJCmXKa4AAat9B.a76cdc92.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FGDJCmXKa4AAat9B.a76cdc92.jpeg&amp;w=3840&amp;q=75">
<blockquote>
<p>Meetup 的形式有可能找到对项目感兴趣的开发者参与进来， 如果要获得广泛的关注度还是需要通过媒体的方式传播， 那还不是我当前要做的事情。</p>
</blockquote>
<p>本来是安排相对宽松的行程能充分准备，但是突然在周四和周五安排了密集的拜访和会议，只得在周五晚上从北京飞到深圳来，凌晨到深圳才住下。</p>
<img alt="大兴机场买的一瓶俄罗斯啤酒" loading="lazy" width="1024" height="768" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FGDE1fLKbQAA4moN.0e120a8f.jpeg&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FGDE1fLKbQAA4moN.0e120a8f.jpeg&amp;w=2048&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FGDE1fLKbQAA4moN.0e120a8f.jpeg&amp;w=2048&amp;q=75">
<h2>我的分享 - 《用 Rust 重新实现 Git 存储服务》</h2>
<p>首先是演讲 Slide 的在线链接 - <a href="https://docs.google.com/presentation/d/1jhfUX5LB7MaH0VC-i_YKS0gnuqYkIpxDlFOIzdUZIUY/edit?usp=sharing">《用 Rust 重新实现 Git 存储服务》</a></p>
<p>其实 <a href="https://github.com/web3infra-foundation/mega">Mega</a> 这个事情讲出来大多数人是不知道我要做什么样的东西。 很多国内开发者对 Monorepo 这个概念还是不太熟悉，就算是国内的一线大厂也没有能力和文化底蕴在公司内推行 Monorepo ，更不用说是关注实现 Monorepo 的实现了。 所以这次我先讲了要做的东西到底是什么， Google 、 Facebook 和 Microsoft 三家公司是怎么实现的，带出来我为什么要重新实现 Git 底层的存储并存到数据库中。</p>
<img alt="你知道到底哪个模式合适你么？" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG13.d0041581.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG13.d0041581.jpg&amp;w=3840&amp;q=75">
<p>实现讲起来就比较乏味， 其中的各种坑是不能用言语表达的，包括到现在对于大 Git 仓库处理过程中内存控制的问题还是没有完全解决。 当然最后我也没有推荐大家去试用，在我来看项目还没有到 0.1 发布的情况下，推荐开发者使用还是不负责的。</p>
<p>很多人对 <a href="https://github.com/web3infra-foundation/mega">Mega</a> 的实现还是关注在 “大”， 我经常也吐槽审美被教育为 “傻大黑粗” 才是最好的。 很多人提出的都是能不能解决大模型开发和训练过程中的版本问题， 这方面要超过 LFS 做出更优雅更有效率的实现还是有些困难， 所以采用 p2p 成为当前的一个重要选择。 P2P 的实现目前 <a href="https://github.com/web3infra-foundation/mega">Mega</a> 还没有做好， 也没有完整的测试计划， 还需要很多的时间去打磨。</p>
<img alt="把一个复杂的项目用简单的架构实现其实也是功力" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG14.b59f3f0d.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG14.b59f3f0d.jpg&amp;w=3840&amp;q=75">
<p><a href="https://github.com/web3infra-foundation/mega">Mega</a> 的架构很简单，我坚持不搞所谓的 Microservice 来去复杂化是希望不管是个人和企业能更容易的部署，包括将来采用 Tauri 打包构建成桌面应用，在我看坚持架构的简单化也是一种美。</p>
<h2>Rust 未来会如何</h2>
<p>我其实在演讲中表达了一个观点，</p>
<blockquote>
<p>Rust 就算是一个内存安全的编程语言，也不能保证开发者写出的代码都是安全的。</p>
</blockquote>
<p>其实我另一个观点没有在公开的场合说，但是下面和几个人聊天时候讲了，</p>
<blockquote>
<p>Rust 是个锤子，不能满眼看到的都是钉子...</p>
</blockquote>
<hr>
<div style="height:400px;width:auto" data-ntpc="YouTubeEmbed"><lite-youtube videoid="DWNlQz-ZLHk" height="400" params="controls=0"></lite-youtube></div>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Rust China Tour - 杭州站活动后记]]></title>
            <link>https://maquanyi.com/articles/20240414-rust-china-tour-hangzhou</link>
            <guid>https://maquanyi.com/articles/20240414-rust-china-tour-hangzhou</guid>
            <pubDate>Sat, 20 Apr 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="活动合影一" loading="lazy" width="1620" height="2160" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185738.1a321aad.jpg&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185738.1a321aad.jpg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185738.1a321aad.jpg&amp;w=3840&amp;q=75">
<p>筹划已久的 <strong>Rust China Tour</strong> 终于 4 月 13 日在杭州完成了首站活动，感谢 <a href="https://openbuild.xyz">OpenBuild</a> <strong>许银</strong> 为活动付出了很多， 并且为这个系列活动起了一个 Fancy 的名字。 每个月一个城市堪比演唱会，叫 Tour 一点不为过， 只是这次活动还是有些匆忙， 没有设计出一些 Tour 的特色。 可能最让开发者满意的是现场 Rust 周边礼物自助不限量， 当然也还要感谢 JetBrains 给寄来的纪念品， 马克杯我自己都忘记拿一个。😂😂😂 活动的微信群名字也被设为 “Rust 中国巡演见面会” ，希望这个群以后能有更多的开发者在里面交流。</p>
<p>整个活动是由蚂蚁集团开源办公室的花肉主持。 五花肉可以说是江浙沪金牌主持， 但是面对下面 P11 的公司大佬有点嘴瓢， 这对于她进军珠三角来说还是一个小挑战 😂😂😂 。 敢于把在活动宣传图片上把自己的名字写在老板前面， 说明她已经具备冲击全国优秀主持人的心理素质了， 嘴瓢可能还是没有理解到这个活动随意性的边界。</p>
<img alt="蚂蚁集团副总裁兼首席技术安全官韦韬" loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185520.ae71f99f.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185520.ae71f99f.jpg&amp;w=3840&amp;q=75">
<p>在一个公司里面， 需要有对技术热衷的领导者才能推动公司内部技术体系的发展， 在蚂蚁集团我看到的是 <a href="https://baike.baidu.com/item/%E9%9F%A6%E9%9F%AC/53148914">韦韬</a> 在不遗余力的推动内部使用 Rust 编程语言； 在华为也有类似的高管， 就是海思总裁、 华为科学家委员会主任 <a href="https://www.huawei.com/cn/executives/board-of-directors/he-tingbo">何廷波</a> ， 在公司内大力推动 Rust 编程语言的使用， 也给了我一个能够从事 Rust 编程语言生态推广工作的机会。</p>
<img alt="Apache HoraeDBPMC Member 、蚂蚁集团技术专家刘家财" loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185552.e11dc9d5.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185552.e11dc9d5.jpg&amp;w=3840&amp;q=75">
<p>首位演讲的嘉宾是 Apache <a href="https://github.com/apache/incubator-horaedb">HoraeDB</a> PMC Member 、蚂蚁集团技术专家 <strong>刘家财</strong>， 演讲的内容是 Rust 中的错误处理。其实 Rust 编程语言的错误机制一直都在被大家诟病， 尤其是大型工程中对于错误处理都显得臃肿和繁琐， 我的感受是其它语言也挺麻烦的， 到现在我也没有看到什么最佳实践是满足所有场景的， 所以 Rust 把方案的选择权交给开发者也是一种合情的处理方式， 是不是合理我也没有资格评价。</p>
<p>另外刘家财是中文播客 <a href="https://rusttalk.github.io">RustTalk</a> 的主理人，这应该是国内第一个专注 Rust 编程语言的播客，欢迎大家订阅收听。 这两天他也有联系我说一起录制一期播客， 我计划从荷兰参加 <a href="https://2024.rustnl.org">RustNL</a> 回来后录一期， 可以顺便讲讲我观察到的上游 Rust 社区情况。</p>
<img alt="华为高级工程师李原" loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185559.5143791b.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185559.5143791b.jpg&amp;w=3840&amp;q=75">
<p>第二个演讲人是我的同事 Rust Complier Team 的 Committer 李原，他演讲的内容是 Rust 并行编译问题。随着越来越多的项目使用 Rust ，编译速度问题越来越被重视， 但是由于对于编译器知识的缺乏以及对 Rust 编译器的理解我是完全没有听懂他说什么，所以早早到会场外偷吃茶歇以及和参会人聊天， 讨论的结果是大家都说这个讲的内容太干，没有怎么听懂。不是我一个人听不懂，挺好的 😂😂😂 。</p>
<p>从我最近在国内的高校里面推广 Rust 编程语言的情况来看， 很少有高校的实验室在做编译器相关的基础研究， 现在更多的是做 AI 相关的，我其实很理解搞 AI 既可以拿项目挣钱也可以发论文评职称。 只是国内高等教育缺乏基础软件的教育不是一年两年的事情了， 太少具有相关基础的人从事这方面的研究， 当然也和前些年互联网公司薪资水涨穿高有很大的关系， 供需市场决定了高校的教育方向。 对于这种境遇我人单势孤， 也无力改变这个既定的状况， 只能在自己的角度能做一点有意义的事情就好。</p>
<img alt="浙江大学研究生侯富中和蔡启航" loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185619.0ac300fa.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185619.0ac300fa.jpg&amp;w=3840&amp;q=75">
<p>浙江大学的两位研究生侯富中和蔡启航两位同学是跟我在一起开发 <a href="https://github.com/web3infra-foundation/mega">Mega</a> 项目， 两位同学分享了使用 Rust 重写 Git 服务端遇到的各种问题以及学习 Rust 的经历。 浙江大学的胡星老师在学校内开始了 Rust 相关的课程， 这两位同学在之前课堂大作业使用 Rust 开发了一个简单的 Git 客户端，课堂答辩的内容录制下来放到了 B 站被我发现， 就找到胡星老师邀请他们来开发 <a href="https://github.com/web3infra-foundation/mega">Mega</a> 。</p>
<p>在校学生学习和使用 Rust 的曲线对比有经验的工程师有明显的差异， 并没有传说中的学习曲线高的问题。 其实这是我一直在思考的一个关键问题， 就是用一个什么样的角度去理解 Rust 才能让学习的人更容易掌握这门编程语言。 当然我现在的方案是让 AI 教我写代码， 让编译器教我做人， 方案简单粗暴但是有效。 从推广 Rust 编程语言的工作角度，这个方案明显是不行，但是我也没有好的解决办法、</p>
<img alt="Fabarta 技术专家胡焰" loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185644.dec03070.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185644.dec03070.jpg&amp;w=3840&amp;q=75">
<p>Fabarta 的技术专家胡焰分享了使用 Rust 开发图数据库 ArchGraph 的经历， ArchGraph 还没有开源， 他提到使用 Rust 开发的时候国内还没有开源的图数据库， 当然现在已经有了好几个， 我最近在看的是蚂蚁开源的 TuGraph ，它的存储底层是 Rust 编写的。 纯 Rust 编程语言的图数据库好像还没有，希望 ArcGraph 早日开源一睹真容。</p>
<p>演讲中提到 bincode 和 serde 在处理 RPC 的时候都有一些问题，我想说可以试试字节开发的 Volo 看是不是符合需求， 只有让这些相关的开源项目相互之间有了交流和依赖， 才能盘活整个国内的生态。后面的活动中，我想应该邀请更多用 Rust 开发实际项目的开发者来分享他们的经验， 这样对于学习 Rust 的人来说是一个很好的参考， 也能够提升大家对 Rust 的信心。 当然要想国内以更快速度发展生态，需要一个 Rust 编程语言的明星项目， 这是可遇而不可求的事情， 但我相信这一天会到来的。</p>
<img alt="南京大学计算机科学与技术系助理研究员冯洋" loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185711.95462161.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185711.95462161.jpg&amp;w=3840&amp;q=75">
<p>最后是南京大学计算机科学与技术系助理研究员冯洋老师带来的 Rust 生态分析的技术研究。 正如冯洋老师所讲， Rust 社区专注在语言特性的实现， 非常缺乏传统测试技术的支持。 我也正在和冯洋老师合作，在 <a href="https://oss-compass.org">Compass</a> 项目的支持下，尝试构建一个 Rust 项目分析平台，计划在开放原子开源基金会的基础设施上部署为开发者提供服务。</p>
<p>冯洋老师代表了国内高校研究者中年轻的一代， 在海外有过留学经历， 有很好技术前瞻性和钻研的精神，希望他们能够改变国内高校科学研究落后的学阀体系， 激励更多的在校生能够参与到开源项目中，在技术领域中有所作为。</p>
<img alt="活动合影二" loading="lazy" width="3072" height="4096" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185745.86e6b11c.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240420185745.86e6b11c.jpg&amp;w=3840&amp;q=75">
<h2>演讲 Slide</h2>
<ol>
<li><a href="https://drive.google.com/file/d/1tcXuNgDqQr_-WoUWxAOS2GDUNMwF1A_U/view?usp=drive_link">Rust 中的错误处理</a></li>
<li><a href="https://drive.google.com/file/d/1Ue5fELh2rLYZqgtOkxUe4-fYcUfHtx91/view?usp=drive_link">Rust 编译效率 - 现状与未来</a></li>
<li><a href="https://drive.google.com/file/d/1lgtL1qtod_8DJavrUxb2E-1syCKhy0Dl/view?usp=drive_link">透过 Git 看 Rust - Trade-off</a></li>
<li><a href="https://drive.google.com/file/d/19CJVI4W9BOaw8NEPtpywHQpTHxPLOXCz/view?usp=drive_link">Rust 在 ArcGraph 中的实践</a></li>
<li><a href="https://drive.google.com/file/d/1yCupoVou2cSLwl28TBIQp_fQrbDInXee/view?usp=drive_link">面向 Rust 生态系统的供应分析与测试平台</a></li>
</ol>
<hr>
<p>对于活动本身来讲， 更大的意义是蚂蚁和华为能够平等的在社区活动中合作， 这在国内头部厂商中应该是首次。 国内的开源也好， 开发者生态都好， 都是以厂商为首的生态隔绝的小圈子， 当然也有在一些基金会下有一些貌合神离的合作， 这样真心的坐下来共同为 Rust 编程语言发展一起努力我相信是首次， 希望能在国内建立起一个榜样， 让大家看到合作的力量。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[我在荷兰赶大集 - IJ-Hallen Amsterdam Noord]]></title>
            <link>https://maquanyi.com/articles/20240504-ij-hallenamsterdam-noord</link>
            <guid>https://maquanyi.com/articles/20240504-ij-hallenamsterdam-noord</guid>
            <pubDate>Sat, 04 May 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152658.fe6c4038.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152658.fe6c4038.jpg&amp;w=3840&amp;q=75">
<p>第二次到阿姆斯特丹终于赶上 NDSM 的集市了， 上次到的时候正好是结束， 问了酒店前台的 MM 才知道这是欧洲最大（欧洲真是人少啊， 说实话这规模还没有任何一个国内三线城市的批发市场大）的二手集市，叫 <strong>IJ-Hallen</strong>。 到的晚上就陆续有人收拾摊位，第二天早上也是 6 点就开始了， 这次住在酒店的二楼，躺在床边就能看到楼下收拾摊位的人。 早期吃完早饭去就去转了一圈， 趁着人不多的时候看看有什么稀奇古怪的东西。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152748.b7530e40.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152748.b7530e40.jpg&amp;w=3840&amp;q=75">
<p>进场的时候才知道要买票，就算是来溜达买东西也要买票，因为来的早必须买早鸟票 10 欧元， 好吧就当是买票看热闹了。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152715.d42cdec4.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152715.d42cdec4.jpg&amp;w=3840&amp;q=75">
<p>市场里面已经布置的七七八八，大多数是卖二手服装和鞋子的，这些东西我都没有啥兴趣，转转看有啥特色点的。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152646.8db5d74a.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152646.8db5d74a.jpg&amp;w=3840&amp;q=75">
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152730.c5bf5ba9.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504152730.c5bf5ba9.jpg&amp;w=3840&amp;q=75">
<p>市场里面很多卖二手唱片的摊位， 大多数都是一对白发苍苍的夫妇在收拾。 很多人都会驻足翻看有没有自己喜欢的。再我溜达回来的时候，竟然有个小伙在这个摊位的一张唱片上找到一个指甲剪，夹在一张他中意的唱片边缘，引来了一阵笑声。白发大爷也被这景象给惊住了， 和他老伴说些什么， 我估计是荷兰语，可能是讨论为什么会有指甲剪放在唱片上。 卖唱片的很多，但是卖旧唱片机的不多， 终于在一个不起眼的摊位看到一台。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504150932.6b8eb8cd.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504150932.6b8eb8cd.jpg&amp;w=3840&amp;q=75">
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151049.54f18f32.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151049.54f18f32.jpg&amp;w=3840&amp;q=75">
<p>当然也毫无意外的发现了几个旧打字机，昨天刚刚在飞机上看了福尔摩斯的电影，我觉得吸引我的不是打字机，而是打字机敲击的那种声音。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151143.9a1f5c5c.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151143.9a1f5c5c.jpg&amp;w=3840&amp;q=75">
<p>经过一个摊位发现在卖各种剪下来的邮票， 都是打过戳的， 估计有人在收集这样的东西， 估计这一袋一袋的应该能掏出来什么稀有的吧。要是在国内，问价的话我估计就是多少钱一袋，活着一斤。然后，我就旁边的箱子里面发现了一个熟悉的东西，哈哈哈。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151202.6f2fa3a2.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151202.6f2fa3a2.jpg&amp;w=3840&amp;q=75">
<p>剩下比较多的摊位就是卖二手照相机的，什么古老的样式都有，我不认得牌子和型号，就找了几个看起上去比较稀有的拍了照片。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151030.a6f47e2a.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151030.a6f47e2a.jpg&amp;w=3840&amp;q=75">
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151103.a760fb9a.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151103.a760fb9a.jpg&amp;w=3840&amp;q=75">
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151117.5ebd2892.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151117.5ebd2892.jpg&amp;w=3840&amp;q=75">
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151129.6e9cf679.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151129.6e9cf679.jpg&amp;w=3840&amp;q=75">
<p>我回到酒店还不到 9 点，后面大批的人陆续坐渡轮到市场里面来逛， 这比参加 KubeCon 的人多太多了。</p>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151533.52ef17de.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151533.52ef17de.jpg&amp;w=3840&amp;q=75">
<hr>
<img alt="NDSM IJ Hallen" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151217.22b091da.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F20240504151217.22b091da.jpg&amp;w=3840&amp;q=75">
<p>自从上次住在 NDSM 以后， 感觉这样的艺术街区相对安静， 合适开会的时候找一个安静的休息场所。 而且旁边的超市可以买到新鲜日期的精酿啤酒， 酒花的香气容易让人熬过时差的艰难。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Buck2 编译 Rust 工程处理 feature 的问题]]></title>
            <link>https://maquanyi.com/articles/buck2-build-rust-features</link>
            <guid>https://maquanyi.com/articles/buck2-build-rust-features</guid>
            <pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>feature 是 Rust 开发过程中非常重要的特性，开发者可以通过 Feature 实现代码编码在不同条件下的行为，以下是一个简单的 Feature 示例：</p>
<pre class="language-rust"><code class="language-rust"><span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token attribute attr-name">#[cfg(feature = <span class="token string">"debug"</span>)]</span>
    <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">"Debug mode is enabled!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token attribute attr-name">#[cfg(feature = <span class="token string">"release"</span>)]</span>
    <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">"Release mode is enabled!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">"Hello, world!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>当编译我们使用 <code>cargo build --release</code> 编译时，会启用 <code>release</code> Feature，输出如下：</p>
<pre class="language-bash"><code class="language-bash">cargo run --features<span class="token operator">=</span><span class="token string">"release"</span>
Finished <span class="token variable"><span class="token variable">`</span>dev<span class="token variable">`</span></span> profile <span class="token punctuation">[</span>unoptimized + debuginfo<span class="token punctuation">]</span> target<span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span>.00s
Running <span class="token variable"><span class="token variable">`</span>target/debug/buck2-build-rust-feature<span class="token variable">`</span></span>
Release mode is enabled<span class="token operator">!</span>
Hello, world<span class="token operator">!</span>
</code></pre>
<p>当我们编写 BUCK 文件的时候如何处理呢，当然我们可以写几个目标任务，例如：</p>
<pre class="language-python"><code class="language-Python">load<span class="token punctuation">(</span><span class="token string">"@prelude//toolchains:rust.bzl"</span><span class="token punctuation">,</span> <span class="token string">"system_rust_toolchain"</span><span class="token punctuation">)</span>

rust_binary<span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"main-release"</span><span class="token punctuation">,</span>
    srcs <span class="token operator">=</span> glob<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"src/**/*.rs"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    crate_root <span class="token operator">=</span> <span class="token string">"src/main.rs"</span><span class="token punctuation">,</span>
    features <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"release"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>

rust_binary<span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"main-debug"</span><span class="token punctuation">,</span>
    srcs <span class="token operator">=</span> glob<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"src/**/*.rs"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    crate_root <span class="token operator">=</span> <span class="token string">"src/main.rs"</span><span class="token punctuation">,</span>
    features <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"debug"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre>
<p>这样 BUCK 文件非常臃肿，当 Feature 有多个且需要组合的时候， 这个方式就变得不再可行。所以我们要编写一个 rule 来处理外部传入的 Feature 参数，实现动态的构建：</p>
<pre class="language-python"><code class="language-Python"><span class="token comment"># config.bzl</span>
<span class="token keyword">def</span> <span class="token function">get_rust_features</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    features_config <span class="token operator">=</span> read_config<span class="token punctuation">(</span><span class="token string">"rust"</span><span class="token punctuation">,</span> <span class="token string">"features"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span>
    
    <span class="token keyword">if</span> features_config <span class="token operator">==</span> <span class="token string">""</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
    <span class="token keyword">return</span> <span class="token punctuation">[</span>f<span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">for</span> f <span class="token keyword">in</span> features_config<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string">","</span><span class="token punctuation">)</span> <span class="token keyword">if</span> f<span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
</code></pre>
<pre class="language-python"><code class="language-Python">load<span class="token punctuation">(</span><span class="token string">"@prelude//toolchains:rust.bzl"</span><span class="token punctuation">,</span> <span class="token string">"system_rust_toolchain"</span><span class="token punctuation">)</span>
load<span class="token punctuation">(</span><span class="token string">"//:config.bzl"</span><span class="token punctuation">,</span> <span class="token string">"get_rust_features"</span><span class="token punctuation">)</span>

rust_binary<span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"rust-features"</span><span class="token punctuation">,</span>
    srcs <span class="token operator">=</span> glob<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"src/**/*.rs"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    crate_root <span class="token operator">=</span> <span class="token string">"src/main.rs"</span><span class="token punctuation">,</span>
    features <span class="token operator">=</span> get_rust_features<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre>
<p>当执行 <code>buck2 build</code> 命令时，使用参数 <code>--config</code> 参数传入 Feature 即可：</p>
<pre class="language-bash"><code class="language-bash">buck2 run //:rust-features                             
Build ID: b28ffbd2-3244-453f-80fc-ffab59e4ee2c
Loading targets.   Remaining     <span class="token number">0</span>/1                                                                                                                                                                          <span class="token number">56</span> <span class="token function">dirs</span> read, <span class="token number">1</span> targets declared
Analyzing targets. Remaining     <span class="token number">0</span>/50                                                                                                                                                                         <span class="token number">35</span> actions, <span class="token number">64</span> artifacts declared
Executing actions. Remaining     <span class="token number">0</span>/7                                                                                                                                                                          <span class="token number">0</span>.8s <span class="token builtin class-name">exec</span> <span class="token function">time</span> total
Command: run.      Finished <span class="token number">1</span> <span class="token builtin class-name">local</span>                                                                                                                                                                                                                                                
Time elapsed: <span class="token number">0</span>.8s
BUILD SUCCEEDED
Starting RUN of <span class="token variable"><span class="token variable">`</span>//:rust-features<span class="token variable">`</span></span>
Running defined output located at: <span class="token variable"><span class="token variable">`</span>/Users/eli/GitMono/buck2-build-rust-feature/buck-out/v2/gen/root/200212f73efcd57d/__rust-features__/rust_features<span class="token variable">`</span></span>
Hello, world<span class="token operator">!</span>

<span class="token comment">#---</span>

buck2 run //:rust-features --config rust.features<span class="token operator">=</span>debug
Build ID: daf63b18-34a0-4a1d-80cc-da10e64f7983
Loading targets.   Remaining     <span class="token number">0</span>/1                                                                                                                                                                          <span class="token number">1</span> targets declared
Analyzing targets. Remaining     <span class="token number">0</span>/1                                                                                                                                                                          <span class="token number">35</span> actions, <span class="token number">64</span> artifacts declared
Executing actions. Remaining     <span class="token number">0</span>/5                                                                                                                                                                          <span class="token number">0</span>.4s <span class="token builtin class-name">exec</span> <span class="token function">time</span> total
Command: run.      Finished <span class="token number">1</span> <span class="token builtin class-name">local</span>                                                                                                                                                                                                                                                
Time elapsed: <span class="token number">0</span>.4s
BUILD SUCCEEDED
Starting RUN of <span class="token variable"><span class="token variable">`</span>//:rust-features<span class="token variable">`</span></span>
Running defined output located at: <span class="token variable"><span class="token variable">`</span>/Users/eli/GitMono/buck2-build-rust-feature/buck-out/v2/gen/root/200212f73efcd57d/__rust-features__/rust_features<span class="token variable">`</span></span>
Debug mode is enabled<span class="token operator">!</span>
Hello, world

<span class="token comment">#---</span>

buck2 run //:rust-features --config rust.features<span class="token operator">=</span>debug,release
File changed: root//README.md
Build ID: a6c85fd0-8e8e-44eb-9a60-48c9223d41e1
Loading targets.   Remaining     <span class="token number">0</span>/1                                                                                                                                                                          <span class="token number">1</span> targets declared
Analyzing targets. Remaining     <span class="token number">0</span>/1                                                                                                                                                                          <span class="token number">35</span> actions, <span class="token number">64</span> artifacts declared
Executing actions. Remaining     <span class="token number">0</span>/5                                                                                                                                                                          <span class="token number">0</span>.4s <span class="token builtin class-name">exec</span> <span class="token function">time</span> total
Command: run.      Finished <span class="token number">1</span> <span class="token builtin class-name">local</span>                                                                                                                                                                                                                                                
Time elapsed: <span class="token number">0</span>.4s
BUILD SUCCEEDED
Starting RUN of <span class="token variable"><span class="token variable">`</span>//:rust-features<span class="token variable">`</span></span>
Running defined output located at: <span class="token variable"><span class="token variable">`</span>/Users/eli/GitMono/buck2-build-rust-feature/buck-out/v2/gen/root/200212f73efcd57d/__rust-features__/rust_features<span class="token variable">`</span></span>
Debug mode is enabled<span class="token operator">!</span>
Release mode is enabled<span class="token operator">!</span>
Hello, world<span class="token operator">!</span>
</code></pre>
<p>当然这只是简单的一个 Feature 示例，实际情况可能要比这个还要复杂，所以下一篇找一个实际项目来编写更复杂的 BUCK 文件。</p>
<blockquote>
<p>代码托管在 GitHub 上，请访问 <a href="https://github.com/genedna/buck2-build-rust-feature">buck2-build-rust-feature</a> 。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Buck2 运行在 Windows 上的一些问题]]></title>
            <link>https://maquanyi.com/articles/buck2-run-windows</link>
            <guid>https://maquanyi.com/articles/buck2-run-windows</guid>
            <pubDate>Wed, 05 Mar 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h3>1. 运行权限问题</h3>
<blockquote>
<p>当 Buck2 在 Windows 上运行并尝试创建符号链接（symlink）时，可能会出现错误 “A required privilege is not held by the client.”（错误代码1314，意为客户端没有所需的特权）。这个错误表明当前用户帐户缺乏创建符号链接的权限。</p>
</blockquote>
<p>解决方法：开启 Windows 开发者模式和修改注册表</p>
<p><strong>启用开发者模式</strong>：</p>
<ol>
<li>打开开发者模式设置： 按下 <strong>Win + R</strong>，输入命令 <code>ms-settings:developers</code> 并回车，或者通过开始菜单进入 “设置” -&gt; “隐私和安全” -&gt; “针对开发人员”（在部分系统中路径为“更新和安全” -&gt; “针对开发人员”）；​</li>
<li>启用开发者模式： 在开发者设置页面，将 “开发人员模式” 开关切换为打开状态。阅读提示后确认启用。启用后，Windows 会授予当前用户帐户创建符号链接的权限​；</li>
</ol>
<p><strong>修改注册表</strong></p>
<ol>
<li>按下 <strong>Win+R</strong> 输入 <code>regedit</code> 打开注册表；</li>
<li>定位：<code>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem</code>；</li>
<li>新建 <code>NtfsAllowSymlinkEvaluation</code> 值为 <code>1</code> （ DWORD值 ）；</li>
</ol>
<p>最后重启电脑即可，不重启可能会无法生效，总之重启大法解决一切。</p>
<h3>2. Python 环境问题</h3>
<ol>
<li>测试系统安装 Python 3.13.2 ，并且需要将 Python 的路径添加到环境变量中，执行 Buck2 的 <code>build</code> 命令时不再报找不到 Python 的错误；</li>
</ol>
<h3>3. 编译 Windows 系统 Library 的问题</h3>
<ol>
<li>编译 <a href="https://crates.io/crates/windows">Windows</a> 的 <code>0.58.0</code> 版本时，要使用 Rust Nightly 版本进行编译，目前的 <code>Stable</code> 版本会报错；</li>
<li>需要在 BUCK 中指定 <code>CARGO_MANIFEST_DIR</code> 路径，尝试使用各种相对路径都会报错，使用 <code>.</code> 反而正常编译；</li>
</ol>
<pre><code>load("@prelude//rust:cargo_package.bzl", "cargo")

cargo.rust_library(
    name = "windows",
    srcs = glob(["src/**/*.rs"]),
    crate = "windows",
    crate_root = "src/lib.rs",
    edition = "2021",
    features = [
        "Win32",
        "Win32_Foundation",
        "Win32_Security",
        "Win32_System",
        "Win32_System_DataExchange",
        "Win32_System_Memory",
        "Win32_System_Threading",
        "Win32_UI",
        "Win32_UI_WindowsAndMessaging",
        "default",
        "std",
    ],
    env = {
        "CARGO_MANIFEST_DIR": ".",
    },
    visibility = ["PUBLIC"],
    deps = [
        "//third-party/rust/crates/windows-core/0.58.0:windows-core",
        "//third-party/rust/crates/windows-targets/0.52.6:windows-targets",
    ],
)
</code></pre>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[使用 Buck2 编译构建 Rust 工程]]></title>
            <link>https://maquanyi.com/articles/buck2-rust-hello-world</link>
            <guid>https://maquanyi.com/articles/buck2-rust-hello-world</guid>
            <pubDate>Mon, 12 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>最近觉得 <del>FacebooK</del> Meta 开源项目都挺良心， 从 React 到 <a href="https://maquanyi.com/articles/facebook-sapling-mercurial-hg-git-monorepo">Sapling</a>， <a href="https://buck2.build">Buck2</a> 等等， 更是有 AI 大模型的 <strong>Llama</strong> ， 让多少国产大模型实现了自主可控。 当然我关注的点都集中在 Rust 开发的项目中， 先是 Monorepo 解决方案 <a href="https://maquanyi.com/articles/facebook-sapling-mercurial-hg-git-monorepo">Sapling</a> 让我感到自己做 Mega 其实路还有很长； 再有 <a href="https://buck2.build">Buck2</a> (Rust 重写 Meta 的构建系统 <a href="https://buck.build">Buck</a>)， 让我明白没有构建系统的 Monorepo &amp; Monolithic 解决方案是不完整的。 之前因为参加 BazelCon 2023 临时加入了 Bazel 不完整的支持， 其实整个项目都还在 Demo 的状态。 春节前最后整理好第一版的数据库结果， 也确定了是使用 Buck2 作为构建系统， 让整个解决方案更加 <strong>Rusty</strong> 。</p>
<p>当边看文档边在 Mega 中加入 <a href="https://buck2.build">Buck2</a> 的支持时， 发现还是有很多概念需要学习， 没办法只能从 <a href="https://github.com/genedna/rust-buck2-example">Hello World</a> 开始尝试构建， 并且记录对 <a href="https://buck2.build">Buck2</a> 的学习。</p>
<h2>按照 Buck2</h2>
<p>首先是安装 <a href="https://buck2.build">Buck2</a> ， 目前还在 pre-release 阶段， 我是直接从 <a href="https://github.com/facebook/buck2/releases">GitHub</a> 直接下载了 <a href="https://buck2.build">Buck2</a> 的二进制文件， 并且放到了 <code>/usr/local/bin</code> 目录下。 开发环境是 Arch Linux， 我提前安装了 <code>clang</code> 和 <code>lld</code> ， 如果还有什么其他的依赖， 请自行安装。</p>
<pre class="language-bash"><code class="language-bash">$ paru -S clang lld
</code></pre>
<h2>创建 Rust 项目， 初始化 Buck2 支持</h2>
<p>首先使用 <code>cargo</code> 命令创建一个 Rust 项目:</p>
<pre class="language-bash"><code class="language-bash">$ cargo new rust-buck2
</code></pre>
<p>然后进入目录初始化 Buck2 支持:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> rust-buck2
$ buck2 init --git --allow-dirty
Reinitialized existing Git repository <span class="token keyword">in</span> /home/eli/GitMono/rust-buck2/.git/
Cloning into <span class="token string">'/home/eli/GitMono/rust-buck2/prelude'</span><span class="token punctuation">..</span>.
remote: Enumerating objects: <span class="token number">19987</span>, done.
remote: Counting objects: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">15879</span>/15879<span class="token punctuation">)</span>, done.
remote: Compressing objects: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">4026</span>/4026<span class="token punctuation">)</span>, done.
remote: Total <span class="token number">19987</span> <span class="token punctuation">(</span>delta <span class="token number">11767</span><span class="token punctuation">)</span>, reused <span class="token number">15823</span> <span class="token punctuation">(</span>delta <span class="token number">11737</span><span class="token punctuation">)</span>, pack-reused <span class="token number">4108</span>
Receiving objects: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">19987</span>/19987<span class="token punctuation">)</span>, <span class="token number">5.50</span> MiB <span class="token operator">|</span> <span class="token number">641.00</span> KiB/s, done.
Resolving deltas: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">15035</span>/15035<span class="token punctuation">)</span>, done.
</code></pre>
<p>这个时候目录结构如下， 添加了 <a href="https://buck2.build">Buck2</a> 的一些配置文件和 <code>prelude</code> 目录，</p>
<pre class="language-bash"><code class="language-bash"><span class="token variable">$tree</span> -a -L <span class="token number">1</span>

├── BUCK
├── .buckconfig
├── .buckroot
├── Cargo.toml
├── .git
├── .gitignore
├── .gitmodules
├── prelude
├── src
└── toolchains
</code></pre>
<h2>编译</h2>
<p>当前 <code>BUCK</code> 文件内容还是初始化的部分， 执行后会在 <code>buck-out</code> 目录下生成 <code>out.txt</code> 文件。</p>
<pre class="language-ini"><code class="language-ini"><span class="token comment"># A list of available rules and their signatures can be found here: https://buck2.build/docs/api/rules/</span>

genrule(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"hello_world",</span>
    <span class="token key attr-name">out</span> <span class="token punctuation">=</span> <span class="token value attr-value">"out.txt",</span>
    <span class="token key attr-name">cmd</span> <span class="token punctuation">=</span> <span class="token value attr-value">"echo BUILT BY BUCK2&gt; $OUT",</span>
)
</code></pre>
<pre class="language-bash"><code class="language-bash">$ buck2 build //<span class="token punctuation">..</span>.
Could not connect to buck2 daemon, starting a new one<span class="token punctuation">..</span>.
Connected to new buck2 daemon.
Build ID: <span class="token number">20514717</span>-e6cc-443e-bfa9-b375e5adabf8
Jobs completed: <span class="token number">42</span>. Time elapsed: <span class="token number">0</span>.0s.
Cache hits: <span class="token number">0</span>%. Commands: <span class="token number">1</span> <span class="token punctuation">(</span>cached: <span class="token number">0</span>, remote: <span class="token number">0</span>, local: <span class="token number">1</span><span class="token punctuation">)</span>
BUILD SUCCEEDED
</code></pre>
<p>对 <code>BUCK</code> 文件进行修改， 使用编译 Rust <code>ToolChain</code> 编译代码，</p>
<pre class="language-ini"><code class="language-ini">rust_binary(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rust-buck2",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">glob(["src/**/*.rs"]),</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"src/main.rs",</span>
)
</code></pre>
<pre class="language-bash"><code class="language-bash">$ buck2 build //:rust-buck2
File changed: root//BUCK
Build ID: 5106983d-7d75-4a7b-ad91-c700a0633851
Jobs completed: <span class="token number">40</span>. Time elapsed: <span class="token number">0</span>.1s.
Cache hits: <span class="token number">0</span>%. Commands: <span class="token number">1</span> <span class="token punctuation">(</span>cached: <span class="token number">0</span>, remote: <span class="token number">0</span>, local: <span class="token number">1</span><span class="token punctuation">)</span>
BUILD SUCCEEDED
$ buck2 run //:rust-buck2
Build ID: f407f0d4-b7b5-4309-960b-01e265a2a02b
Jobs completed: <span class="token number">3</span>. Time elapsed: <span class="token number">0</span>.0s.
BUILD SUCCEEDED
Hello, world<span class="token operator">!</span>
</code></pre>
<h2>修改 Toolchain 配置</h2>
<p>使用 <code>prelude</code> 目录下的规则文件修改 <code>toolchains</code> 目录下的 <code>BUCK</code> 文件， 删除默认的 <code>load("@prelude//toolchains:demo.bzl", "system_demo_toolchains")</code> 和 <code>system_demo_toolchains()</code> ，</p>
<pre class="language-ini"><code class="language-ini">load("@prelude//toolchains:rust.bzl", "system_rust_toolchain")
load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
load("@prelude//toolchains:cxx.bzl", "system_cxx_toolchain")
load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")

system_genrule_toolchain(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"genrule",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">["PUBLIC"],</span>
)

system_rust_toolchain(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rust",</span>
    <span class="token key attr-name">default_edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2021",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">["PUBLIC"],</span>
)

system_cxx_toolchain(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"cxx",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">["PUBLIC"],</span>
)

system_python_bootstrap_toolchain(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"python_bootstrap",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">["PUBLIC"],</span>
)
</code></pre>
<p>目前这个 Toolchain 可以用于更多的项目， 例如 C++ 项目， Python 项目等等。</p>
<hr>
<blockquote>
<p>测试的项目在 <a href="https://github.com/genedna/rust-buck2-example">GitHub - Rust Buck2 Example</a> ， 后续会持续更新。</p>
</blockquote>
<blockquote>
<p><a href="https://github.com/genedna/rust-buck2-monorepo-template">Rust Monorepo Template with Buck2</a> 中， 正在持续尝试构建一个完整的 <code>Rust Monorepo</code> 工程模板， 通过 <code>Buck2</code> 和 <code>Reindeer</code> 管理整个工程的构建和测试， 可以从这个项目中开始你的 <code>Rust Monorepo</code> 之旅。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[使用 Buck2 构建 Rust Monorepo 单元测试]]></title>
            <link>https://maquanyi.com/articles/buck2-rust-monorepo-test</link>
            <guid>https://maquanyi.com/articles/buck2-rust-monorepo-test</guid>
            <pubDate>Fri, 16 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>实现了 Monorepo 基本的编译能力后，开始探索使用 Buck2 的测试功能。看文档还是有点复杂，现在还不理解 <code>Test Runner</code> 这个概念，从表面上看是不是可以把 <code>Docker</code> 封装为一个 <code>Test Runner</code>，然后在 <code>Buck2</code> 中使用 <code>Test Runner</code> 运行测试？还是先把单元测试部分处理好吧...</p>
<p>首先是在在 <code>toolchains</code> 目录下的 <code>BUCK</code> 文件加入 <code>remote_test_execution_toolchain</code> 这个 <code>Rule</code>，</p>
<pre class="language-rust"><code class="language-rust"><span class="token function">load</span><span class="token punctuation">(</span><span class="token string">"@prelude//toolchains:remote_test_execution.bzl"</span><span class="token punctuation">,</span> <span class="token string">"remote_test_execution_toolchain"</span><span class="token punctuation">)</span>

<span class="token function">remote_test_execution_toolchain</span><span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"remote_test_execution"</span><span class="token punctuation">,</span>
    visibility <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"PUBLIC"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre>
<p>在代码中加入测试用例，</p>
<pre class="language-rust"><code class="language-rust"><span class="token attribute attr-name">#[cfg(test)]</span>
<span class="token keyword">mod</span> <span class="token module-declaration namespace">tests</span> <span class="token punctuation">{</span>
    <span class="token keyword">use</span> <span class="token keyword">super</span><span class="token punctuation">::</span><span class="token operator">*</span><span class="token punctuation">;</span>

    <span class="token attribute attr-name">#[test]</span>
    <span class="token keyword">fn</span> <span class="token function-definition function">version_match_works</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token macro property">assert!</span><span class="token punctuation">(</span><span class="token function">version_match</span><span class="token punctuation">(</span><span class="token string">"1.0.0"</span><span class="token punctuation">,</span> <span class="token string">"&gt;= 1.0.0"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">assert!</span><span class="token punctuation">(</span><span class="token function">version_match</span><span class="token punctuation">(</span><span class="token string">"1.0.0"</span><span class="token punctuation">,</span> <span class="token string">"&gt;= 1.0.0, &lt; 2.0.0"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">assert!</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">version_match</span><span class="token punctuation">(</span><span class="token string">"1.0.0"</span><span class="token punctuation">,</span> <span class="token string">"&gt;= 2.0.0"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>因为 <code>rust_library</code> 和 <code>rust_test</code> 都要用相同的 <code>deps</code>， 所以在 <code>rust_library</code> 中加入 <code>shared_deps</code> 避免重复，感觉这个实现的方案也不优雅，但是还没有找到更合适的。</p>
<pre class="language-rust"><code class="language-rust">shared_deps <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token string">"//third-party:semver"</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span>

<span class="token function">rust_library</span><span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"rust-library"</span><span class="token punctuation">,</span>
    srcs <span class="token operator">=</span> <span class="token function">glob</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"src/**/*.rs"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    crate_root <span class="token operator">=</span> <span class="token string">"src/lib.rs"</span><span class="token punctuation">,</span>
    tests <span class="token operator">=</span> <span class="token punctuation">[</span>
        <span class="token string">"//projects/rust-library:rust-library-test"</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
    deps <span class="token operator">=</span> shared_deps<span class="token punctuation">,</span>
    visibility <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"PUBLIC"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>

<span class="token function">rust_test</span><span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"rust-library-test"</span><span class="token punctuation">,</span>
    srcs <span class="token operator">=</span> <span class="token function">glob</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"src/**/*.rs"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    crate_root <span class="token operator">=</span> <span class="token string">"src/lib.rs"</span><span class="token punctuation">,</span>
    deps <span class="token operator">=</span> shared_deps<span class="token punctuation">,</span>
    visibility <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"PUBLIC"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre>
<p>执行 <code>buck2 test</code> 命令进行测试，</p>
<pre class="language-bash"><code class="language-bash">$ buck2 <span class="token builtin class-name">test</span> //<span class="token punctuation">..</span>.
✓ Pass: root//projects/rust-library:rust-library-test <span class="token punctuation">(</span><span class="token number">0</span>.0s<span class="token punctuation">)</span>
---- STDOUT ----

running <span class="token number">1</span> <span class="token builtin class-name">test</span>
<span class="token builtin class-name">test</span> tests::version_match_works <span class="token punctuation">..</span>. ok

<span class="token builtin class-name">test</span> result: ok. <span class="token number">1</span> passed<span class="token punctuation">;</span> <span class="token number">0</span> failed<span class="token punctuation">;</span> <span class="token number">0</span> ignored<span class="token punctuation">;</span> <span class="token number">0</span> measured<span class="token punctuation">;</span> <span class="token number">0</span> filtered out<span class="token punctuation">;</span> finished <span class="token keyword">in</span> <span class="token number">0</span>.00s


---- STDERR ----

Build ID: dbc61ebd-700f-49a4-8d33-28d5bd535051
Jobs completed: <span class="token number">4</span>. Time elapsed: <span class="token number">0</span>.0s.
Tests finished: Pass <span class="token number">1</span>. Fail <span class="token number">0</span>. Fatal <span class="token number">0</span>. Skip <span class="token number">0</span>. Build failure <span class="token number">0</span>
</code></pre>
<hr>
<blockquote>
<p><a href="https://github.com/genedna/rust-buck2-monorepo-template">Rust Monorepo Template with Buck2</a> 中， 正在持续尝试构建一个完整的 <code>Rust Monorepo</code> 工程模板， 通过 <code>Buck2</code> 和 <code>Reindeer</code> 管理整个工程的构建和测试， 可以从这个项目中开始你的 <code>Rust Monorepo</code> 之旅。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[使用 Buck2 和 Reindeer 构建 Rust Monorepo 工程]]></title>
            <link>https://maquanyi.com/articles/buck2-rust-reindeer-monorepo</link>
            <guid>https://maquanyi.com/articles/buck2-rust-reindeer-monorepo</guid>
            <pubDate>Thu, 15 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<blockquote>
<p>Meta employs a very large monorepo, consisting of a variety of programming languages, including C++, Python, Rust, Kotlin, Swift, Objective-C, Haskell, OCaml, and more. Google employs a different but functionally similar monorepo.</p>
</blockquote>
<p>Buck2 文档中有一篇 <a href="https://github.com/facebook/buck2/blob/main/docs/why.md">Why</a> ， 第一段说明了 <a href="https://buck.build">Buck</a> 、 <a href="https://buck2.build">Buck2</a> 和 <a href="https://bazel.build">Bazel</a> 存在的关键原因， 就是为了公司内部的 <strong>Monorepo</strong> 提供更好的构建和测试支持。</p>
<p>采用 <code>Monorepo</code> 的方式组织大型 <code>Rust</code> 工程， 可以替代 <code>Cargo Workspaces</code> 。 当然更不推荐的模式包括 <code>Git Submodules</code>、 <code>Git Subtree</code> 或者多个代码仓库（<strong>Polyrepo</strong>）的方式， 核心的关键是要把所有的代码都组织在一起， 在任何代码的提交后马上进行整个工程的构建和测试， 并且让所有开发者都有代码的可见性。</p>
<blockquote>
<p><a href="https://github.com/web3infra-foundation/mega">Mega</a> 项目目前在整体个工程组织中就存在严重的问题， 目前所做的研究是为了按照 <strong>Monorepo</strong> 的方式重新组织工程做的准备。</p>
</blockquote>
<p>根据 <a href="https://github.com/facebookincubator/reindeer">Reindeer</a> 中 <code>Example</code> 的设计， 使用以下目录结构来组织代码：</p>
<pre class="language-bash"><code class="language-bash">├── project
├── third-party
│&nbsp;&nbsp; ├── fixups
│&nbsp;&nbsp; ├── macros
│&nbsp;&nbsp; ├── <span class="token function">top</span>
│&nbsp;&nbsp; └── vendor
└── toolchains
</code></pre>
<p>所有开发的工程都可以放到 project 中， 项目的依赖通过 <a href="https://github.com/facebookincubator/reindeer">Reindeer</a> 在 <code>third-party</code> 中进行管理。 如果工程中还有其他语言的项目， 可以在 <code>third-party</code> 中根据语言构建新的目录， 例如 <code>third-party/python</code>、 <code>third-party/swift</code> 等。 <code>project</code> 下的目录可以根据工程层次进行划分, 层级并没有限制。</p>
<p>在 <code>project</code> 中使用的 <code>crates</code> 在 <code>third-party</code> 目录的 <code>Cargo.toml</code> 进行管理，</p>
<pre class="language-toml"><code class="language-toml"><span class="token punctuation">[</span><span class="token table class-name">dependencies</span><span class="token punctuation">]</span>
<span class="token key property">rand</span> <span class="token punctuation">=</span> <span class="token string">'0.8.5'</span>
<span class="token key property">semver</span> <span class="token punctuation">=</span> <span class="token string">'1.0.21'</span>
</code></pre>
<p>执行 <a href="https://github.com/facebookincubator/reindeer">Reindeer</a> 后在 <code>third-party</code> 更新 <code>BUCK</code> 文件。</p>
<pre class="language-bash"><code class="language-bash">$ reindeer --third-party-dir third-party vendor
$ reindeer --third-party-dir third-party buckify
</code></pre>
<p>在 <code>BUCK</code> 文件中产生的 <code>alias</code> 是暴露给 <code>project</code> 使用， 例如 <code>third-party</code> 中的 <code>rand</code> 和 <code>semver</code> 两个 <code>alias</code>，</p>
<pre class="language-rust"><code class="language-rust"><span class="token function">alias</span><span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"semver"</span><span class="token punctuation">,</span>
    actual <span class="token operator">=</span> <span class="token string">":semver-1.0.21"</span><span class="token punctuation">,</span>
    visibility <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"PUBLIC"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>

<span class="token function">alias</span><span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"rand"</span><span class="token punctuation">,</span>
    actual <span class="token operator">=</span> <span class="token string">":rand-0.8.5"</span><span class="token punctuation">,</span>
    visibility <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"PUBLIC"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
</code></pre>
<p>在 <code>project</code> 中项目的 <code>BUCK</code> 文件中可以直接使用 <code>third-party</code> 中的 <code>alias</code>。</p>
<pre class="language-rust"><code class="language-rust"><span class="token function">rust_binary</span><span class="token punctuation">(</span>
    name <span class="token operator">=</span> <span class="token string">"rust-buck2"</span><span class="token punctuation">,</span>
    srcs <span class="token operator">=</span> <span class="token function">glob</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"src/**/*.rs"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    crate_root <span class="token operator">=</span> <span class="token string">"src/main.rs"</span><span class="token punctuation">,</span>
    deps <span class="token operator">=</span> <span class="token punctuation">[</span>
        <span class="token string">"//third-party:rand"</span><span class="token punctuation">,</span>
        <span class="token string">"//projects/rust-library:rust-library"</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span>
<span class="token punctuation">)</span>
</code></pre>
<p>当然也可以使用 <code>//projects/rust-library:rust-library</code> 的方式来引用 <code>project</code> 中的 <code>library</code>。</p>
<p>虽然使用 <a href="https://github.com/facebookincubator/reindeer">Reindeer</a> 通过 <code>third-party</code> 维护 <code>crates</code> 的依赖比较麻烦， 但在大型公司内部都对 <code>third-party</code> 有严格的使用要求，除了安全、性能等原因，还有合规方面的很多考量。 通过对 <code>third-party</code> 的控制， 可以有效的管理开发者对外部 <code>crates</code> 的使用， 提升整个工程流程的可控性。</p>
<hr>
<blockquote>
<p><a href="https://github.com/genedna/rust-buck2-monorepo-template">Rust Monorepo Template with Buck2</a> 中， 正在持续尝试构建一个完整的 <code>Rust Monorepo</code> 工程模板， 通过 <code>Buck2</code> 和 <code>Reindeer</code> 管理整个工程的构建和测试， 可以从这个项目中开始你的 <code>Rust Monorepo</code> 之旅。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[使用 Buck2 和 Reindeer 生成 Rust 项目 BUCK]]></title>
            <link>https://maquanyi.com/articles/buck2-rust-reindeer</link>
            <guid>https://maquanyi.com/articles/buck2-rust-reindeer</guid>
            <pubDate>Wed, 14 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>不管是 <a href="https://buck2.build">Buck2</a> 还是 <a href="https://bazel.build">Bazel</a> 构建 Rust 工程时都没有使用 <code>Cargo.toml</code> 文件， 这就意味着需要手动维护依赖关系， 这对于 Rust 项目来说是一个非常大的挑战。 Meta 提供的 <a href="https://github.com/facebookincubator/reindeer">Reindeer</a> 可以帮助自动生成 BUCK 文件， 可以解决这部分的问题。</p>
<h2>在示例工程中加入 rand 依赖</h2>
<p>首先在项目中的 <code>Cargo.toml</code> 文件中加入 <code>rand</code> 依赖， 在 <code>src/main.rs</code> 文件中使用 <code>rand</code> 生成一个随机数。</p>
<pre class="language-rust"><code class="language-rust"><span class="token keyword">use</span> <span class="token namespace">rand<span class="token punctuation">::</span></span><span class="token punctuation">{</span>thread_rng<span class="token punctuation">,</span> <span class="token class-name">Rng</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> <span class="token keyword">mut</span> rng <span class="token operator">=</span> <span class="token function">thread_rng</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> x<span class="token punctuation">:</span> <span class="token keyword">u32</span> <span class="token operator">=</span> rng<span class="token punctuation">.</span><span class="token function">gen</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">"A random value: {}"</span><span class="token punctuation">,</span> x<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>虽然是使用 <code>cargo build</code> 或者 <code>cargo run</code> 命令都可以正常执行， 但是如果直接使用 <code>buck2 build</code> 命令构建项目， 会报错：</p>
<pre class="language-bash"><code class="language-bash">$ buck2 build //:rust-buck2
File changed: root//Cargo.toml
File changed: root//Cargo.lock
File changed: root//target/debug/.fingerprint/rust-buck2-fe6911eada5a5db6
<span class="token number">387</span> additional <span class="token function">file</span> change events
Action failed: root//:rust-buck2 <span class="token punctuation">(</span>rustc bin-pic-static_pic-link/rust_buck2-link bin,pic,link <span class="token punctuation">[</span>diag<span class="token punctuation">]</span><span class="token punctuation">)</span>
Local <span class="token builtin class-name">command</span> returned non-zero <span class="token builtin class-name">exit</span> code <span class="token number">1</span>
Reproduce locally: <span class="token variable"><span class="token variable">`</span><span class="token function">env</span> -- 'BUCK_SCRATCH_PATH<span class="token operator">=</span>buck-out/v2/tmp/root/524f8da68ea2a374/__rust-buck2__/rustc/_buck_5339cff94 <span class="token punctuation">..</span>.<span class="token operator">&lt;</span>omitted<span class="token operator">&gt;</span><span class="token punctuation">..</span>. ck-out/v2/gen/root/524f8da68ea2a374/__rust-buck2__/bin-pic-static_pic-link/rust_buck2-link-diag.args <span class="token punctuation">(</span>run <span class="token variable">`</span></span>buck2 log what-failed<span class="token variable"><span class="token variable">`</span> to get the full <span class="token builtin class-name">command</span><span class="token punctuation">)</span><span class="token variable">`</span></span>
stdout:
stderr:
error<span class="token punctuation">[</span>E0432<span class="token punctuation">]</span>: unresolved <span class="token function">import</span> <span class="token variable"><span class="token variable">`</span>rand<span class="token variable">`</span></span>
 --<span class="token operator">&gt;</span> ./src/main.rs:1:5
  <span class="token operator">|</span>
<span class="token number">1</span> <span class="token operator">|</span> use rand::<span class="token punctuation">{</span>thread_rng, Rng<span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token operator">|</span>     ^^^^ use of undeclared crate or module <span class="token variable"><span class="token variable">`</span>rand<span class="token variable">`</span></span>


error: aborting due to <span class="token number">1</span> previous error


For <span class="token function">more</span> information about this error, try <span class="token variable"><span class="token variable">`</span>rustc --explain E0432<span class="token variable">`</span></span><span class="token builtin class-name">.</span>

Build ID: 41b05c41-4e1d-45f2-a35b-ca2bb7ac0565
Jobs completed: <span class="token number">6</span>. Time elapsed: <span class="token number">0</span>.0s.
Cache hits: <span class="token number">0</span>%. Commands: <span class="token number">1</span> <span class="token punctuation">(</span>cached: <span class="token number">0</span>, remote: <span class="token number">0</span>, local: <span class="token number">1</span><span class="token punctuation">)</span>
BUILD FAILED
Failed to build <span class="token string">'root//:rust-buck2 (prelude//platforms:default#524f8da68ea2a374)'</span>
</code></pre>
<p>说明此时 <a href="https://buck2.build">Buck2</a> 并不知道如何处理 <code>rand</code> 依赖。</p>
<h2>使用 Reindeer 生成 BUCK 文件</h2>
<p>首先在系统中安装 <a href="https://github.com/facebookincubator/reindeer">Reindeer</a>，</p>
<pre class="language-bash"><code class="language-bash">$ cargo <span class="token function">install</span> --locked --git https://github.com/facebookincubator/reindeer reindeer
</code></pre>
<p>根据 <a href="https://github.com/facebookincubator/reindeer">Reindeer</a> 的文档， 可以使用 <code>reindeer buckify</code> 命令生成 BUCK 文件。</p>
<pre class="language-bash"><code class="language-bash">$ reindeer buckify
</code></pre>
<p>这时候发现 <code>BUCK</code> 文件中多了很多内容， 但是之前的 <code>rust_binary</code> 的 <code>rust-buck2</code> 任务被移除了，</p>
<pre class="language-ini"><code class="language-ini"><span class="token comment"># @generated by `reindeer buckify`</span>

load("@prelude//rust:cargo_buildscript.bzl", "buildscript_run")
load("@prelude//rust:cargo_package.bzl", "cargo")

http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"cfg-if-1.0.0.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"cfg-if-1.0.0",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/cfg-if/1.0.0/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"cfg-if-1.0.0",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":cfg-if-1.0.0.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"cfg_if",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"cfg-if-1.0.0.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2018",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"getrandom-0.2.12.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"getrandom-0.2.12",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/getrandom/0.2.12/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"getrandom-0.2.12",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":getrandom-0.2.12.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"getrandom",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"getrandom-0.2.12.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2018",</span>
    <span class="token key attr-name">features</span> <span class="token punctuation">=</span> <span class="token value attr-value">["std"],</span>
    <span class="token key attr-name">platform</span> <span class="token punctuation">=</span> <span class="token value attr-value">{</span>
        "linux-arm64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "linux-x86_64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "macos-arm64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "macos-x86_64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
    },
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
    <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":cfg-if-1.0.0"],</span>
)

http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"libc-0.2.153.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"libc-0.2.153",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/libc/0.2.153/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"libc-0.2.153",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"libc",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"libc-0.2.153.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2015",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"ppv-lite86-0.2.17.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"ppv-lite86-0.2.17",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/ppv-lite86/0.2.17/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"ppv-lite86-0.2.17",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":ppv-lite86-0.2.17.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"ppv_lite86",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"ppv-lite86-0.2.17.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2018",</span>
    <span class="token key attr-name">features</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        "simd",
        "std",
    ],
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

alias(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand",</span>
    <span class="token key attr-name">actual</span> <span class="token punctuation">=</span> <span class="token value attr-value">":rand-0.8.5",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">["PUBLIC"],</span>
)

http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/rand/0.8.5/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":rand-0.8.5.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2018",</span>
    <span class="token key attr-name">features</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        "alloc",
        "default",
        "getrandom",
        "libc",
        "rand_chacha",
        "std",
        "std_rng",
    ],
    <span class="token key attr-name">platform</span> <span class="token punctuation">=</span> <span class="token value attr-value">{</span>
        "linux-arm64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "linux-x86_64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "macos-arm64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "macos-x86_64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
    },
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
    <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        ":rand_chacha-0.3.1",
        ":rand_core-0.6.4",
    ],
)

http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_chacha-0.3.1.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_chacha-0.3.1",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/rand_chacha/0.3.1/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_chacha-0.3.1",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":rand_chacha-0.3.1.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_chacha",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_chacha-0.3.1.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2018",</span>
    <span class="token key attr-name">features</span> <span class="token punctuation">=</span> <span class="token value attr-value">["std"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
    <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        ":ppv-lite86-0.2.17",
        ":rand_core-0.6.4",
    ],
)

http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_core-0.6.4.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_core-0.6.4",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/rand_core/0.6.4/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)

cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_core-0.6.4",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":rand_core-0.6.4.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_core",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand_core-0.6.4.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2018",</span>
    <span class="token key attr-name">features</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        "alloc",
        "getrandom",
        "std",
    ],
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
    <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":getrandom-0.2.12"],</span>
)
</code></pre>
<p>只能在 <code>BUCK</code> 文件中手动加入 <code>rust_binary</code> 任务， 并且加入 <code>rand</code> 依赖。</p>
<pre class="language-ini"><code class="language-ini">rust_binary(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rust-buck2",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">glob(["src/**/*.rs"]),</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"src/main.rs",</span>
    <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        ":rand",
    ],
)
</code></pre>
<p>再次使用 <code>buck2 build</code> 命令构建项目或 <code>buck2 run</code> 命令运行项目， 都可以正常执行，</p>
<pre class="language-bash"><code class="language-bash">$ buck2 build //:rust-buck2
Build ID: 6b33c530-8f47-4c65-84d9-08586c0b56fa
Network: Up: 0B  Down: 910KiB
Jobs completed: <span class="token number">154</span>. Time elapsed: <span class="token number">21</span>.6s.
Cache hits: <span class="token number">0</span>%. Commands: <span class="token number">21</span> <span class="token punctuation">(</span>cached: <span class="token number">0</span>, remote: <span class="token number">0</span>, local: <span class="token number">21</span><span class="token punctuation">)</span>
BUILD SUCCEEDED
</code></pre>
<pre class="language-bash"><code class="language-bash">$ buck2 run //:rust-buck2
Build ID: 5ac5973f-423a-4248-be29-2935bdfdb606
Network: Up: 0B  Down: 0B
Jobs completed: <span class="token number">3</span>. Time elapsed: <span class="token number">0</span>.0s.
BUILD SUCCEEDED
A random value: <span class="token number">3295093910</span>
</code></pre>
<h2>deps, alias, http_archive 和 cargo.rust_library</h2>
<pre class="language-ini"><code class="language-ini">alias(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand",</span>
    <span class="token key attr-name">actual</span> <span class="token punctuation">=</span> <span class="token value attr-value">":rand-0.8.5",</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">["PUBLIC"],</span>
)
</code></pre>
<p>在 <code>BUCK</code> 文件中可以看到一个 <code>alias</code> 指向了 <code>rand-0.8.5</code> 这个 <code>cargo.rust_library</code>， 如果把 <code>rust_binary</code> 任务中的 <code>:rand</code> 替换成 <code>:rand-0.8.5</code> 也是可以正常执行的。 这里的 <code>visibility</code> 是指定了 <code>alias</code> 的可见性， 如果不指定的话默认是 <code>PUBLIC</code>。</p>
<pre class="language-ini"><code class="language-ini">cargo.rust_library(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5",</span>
    <span class="token key attr-name">srcs</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":rand-0.8.5.crate"],</span>
    <span class="token key attr-name">crate</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand",</span>
    <span class="token key attr-name">crate_root</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5.crate/src/lib.rs",</span>
    <span class="token key attr-name">edition</span> <span class="token punctuation">=</span> <span class="token value attr-value">"2018",</span>
    <span class="token key attr-name">features</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        "alloc",
        "default",
        "getrandom",
        "libc",
        "rand_chacha",
        "std",
        "std_rng",
    ],
    <span class="token key attr-name">platform</span> <span class="token punctuation">=</span> <span class="token value attr-value">{</span>
        "linux-arm64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "linux-x86_64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "macos-arm64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
        "macos-x86_64": dict(
            <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[":libc-0.2.153"],</span>
        ),
    },
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
    <span class="token key attr-name">deps</span> <span class="token punctuation">=</span> <span class="token value attr-value">[</span>
        ":rand_chacha-0.3.1",
        ":rand_core-0.6.4",
    ],
)
</code></pre>
<ol>
<li><code>cargo.rust_library</code> 是文件头 <code>load("@prelude//rust:cargo_package.bzl", "cargo")</code> 中定义的一个函数， 用于指定一个 Rust 依赖库，可以在工程目录下 <code>prelude/rust/cargo_package.bzl</code> 文件中找到这个函数的定义。</li>
<li><code>alias</code> 的引入为 <strong>crate</strong> 升级提供了便利， 同时代码中的 <code>deps</code> 也可以更加清晰， 和 <code>Cargo.toml</code> 文件中的 <code>dependencies</code> 更加匹配， 让 Rust 开发者更加容易理解。</li>
</ol>
<pre class="language-ini"><code class="language-ini">http_archive(
    <span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5.crate",</span>
    <span class="token key attr-name">sha256</span> <span class="token punctuation">=</span> <span class="token value attr-value">"34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404",</span>
    <span class="token key attr-name">strip_prefix</span> <span class="token punctuation">=</span> <span class="token value attr-value">"rand-0.8.5",</span>
    <span class="token key attr-name">urls</span> <span class="token punctuation">=</span> <span class="token value attr-value">["https://crates.io/api/v1/crates/rand/0.8.5/download"],</span>
    <span class="token key attr-name">visibility</span> <span class="token punctuation">=</span> <span class="token value attr-value">[],</span>
)
</code></pre>
<p><a href="https://buck2.build/docs/api/rules/#http_archive">http_archive</a> 是一个 <a href="https://buck2.build">Buck2</a> 的内置 <strong>Rule</strong>， 用于从网络上下载文件， 这里用于下载 <code>rand-0.8.5</code> 这个 crate 的源码， 并且指定了 <code>sha256</code> 值， 用于校验下载的文件是否正确。</p>
<hr>
<blockquote>
<p>测试的项目在 <a href="https://github.com/genedna/rust-buck2-example">GitHub - Rust Buck2 Example</a> ， 后续会持续更新。</p>
</blockquote>
<blockquote>
<p><a href="https://github.com/genedna/rust-buck2-monorepo-template">Rust Monorepo Template with Buck2</a> 中， 正在持续尝试构建一个完整的 <code>Rust Monorepo</code> 工程模板， 通过 <code>Buck2</code> 和 <code>Reindeer</code> 管理整个工程的构建和测试， 可以从这个项目中开始你的 <code>Rust Monorepo</code> 之旅。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Buck2 的 select 和操作系统]]></title>
            <link>https://maquanyi.com/articles/buck2-rust-select-os</link>
            <guid>https://maquanyi.com/articles/buck2-rust-select-os</guid>
            <pubDate>Thu, 06 Mar 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>Select</h2>
<p>在 BUCK 文件中使用 select 来根据不同的条件处理不同的逻辑，比如下面的例子：</p>
<pre><code>load("@prelude//toolchains:rust.bzl", "system_rust_toolchain")

CRATE_ROOTS = {
    "ovr_config//os:linux": "src/main-linux.rs",
    "ovr_config//os:windows": "src/main-windows.rs",
    "ovr_config//os:macos": "src/main-mac.rs",
    "DEFAULT": "src/main.rs",
}

rust_binary(
    name = "select-os",
    srcs = select({
        "ovr_config//os:linux": ["src/main-linux.rs"],
        "ovr_config//os:windows": ["src/main-windows.rs"],
        "ovr_config//os:macos": ["src/main-mac.rs"],
        "DEFAULT": ["src/main.rs"],
    }),
    crate_root = select(CRATE_ROOTS),
    features = [],
)
</code></pre>
<p>在上面的例子中，我们使用 select 来选择不同的源文件，根据不同的操作系统来选择不同的源文件，这样就可以在不同的操作系统上编译不同的代码。 在 Mac 上执行的时候，输出结果如下：</p>
<pre class="language-bash"><code class="language-bash">buck2 run //:select-os
File changed: root//.git/index.lock
File changed: root//.git/index
File changed: root//.git/objects/44/f609e7b954f1d6db51c2f902811e36c07cbbdc
<span class="token number">31</span> additional <span class="token function">file</span> change events
Build ID: 3cd811e0-9de6-4ea0-b64e-4f6efcd221bb
Loading targets.   Remaining     <span class="token number">0</span>/1                                                                                         <span class="token number">1</span> <span class="token function">dirs</span> read, <span class="token number">1</span> targets declared
Command: run.                                                                                                                                                                                     
Time elapsed: <span class="token number">0</span>.0s
BUILD SUCCEEDED
Starting RUN of <span class="token variable"><span class="token variable">`</span>//:select-os<span class="token variable">`</span></span>
Running defined output located at: <span class="token variable"><span class="token variable">`</span>/Users/eli/GitMono/buck2/buck2-build-rust-select-os/buck-out/v2/gen/root/200212f73efcd57d/__select-os__/select_os<span class="token variable">`</span></span>
Hello, world form mac<span class="token operator">!</span>
</code></pre>
<ol>
<li>Buck2 根据所在的操作系统自动选择了不同的源文件，然后编译了不同的代码；</li>
<li><code>select</code> 会根据是从一个 <code>dict</code> 选择一个值用于 <code>srcs</code> 的结果；</li>
<li><code>crate_root</code> 是显示指定程序的入口文件，默认可能是 <code>src/main.rs</code> 或 <code>src/lib.rs</code>。</li>
<li><code>DEFAULT</code> 是 <code>select</code> 的一个默认写法，用于没有选择到到任何值的时候使用，但是 <code>DEFAULT</code> 并不是关键字；</li>
</ol>
<h3>ovr_config</h3>
<p><code>ovr_conifg</code> 是在 <code>.buckconfig</code> 定义的别名：</p>
<pre class="language-ini"><code class="language-ini"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">cell_aliases</span><span class="token punctuation">]</span></span>
 <span class="token key attr-name">config</span> <span class="token punctuation">=</span> <span class="token value attr-value">prelude</span>
 <span class="token key attr-name">ovr_config</span> <span class="token punctuation">=</span> <span class="token value attr-value">prelude</span>
</code></pre>
<p>所以把 <code>BUCK</code> 进行修改同样正常运行：</p>
<pre><code>CRATE_ROOTS = {
    "prelude//os:linux": "src/main-linux.rs",
    "prelude//os:windows": "src/main-windows.rs",
    "prelude//os:macos": "src/main-mac.rs",
    "DEFAULT": "src/main.rs",
}

rust_binary(
    name = "select-os",
    srcs = select({
        "ovr_config//os:linux": ["src/main-linux.rs"],
        "ovr_config//os:windows": ["src/main-windows.rs"],
        "ovr_config//os:macos": ["src/main-mac.rs"],
        "DEFAULT": ["src/main.rs"],
    }),
    crate_root = select(CRATE_ROOTS),
    features = [],
)
</code></pre>
<p>还有很多要研究的问题，先记录一下：</p>
<ol>
<li>如何配置 Rust 交叉编译环境？</li>
<li><code>ovr_config</code> 是什么样的写法，约定俗成么？</li>
</ol>
<blockquote>
<p>代码托管在 GitHub 上，请访问 <a href="https://github.com/genedna/buck2-build-rust-select-os">buck2-build-select-os</a> 。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[在 Windows 11 上配置 SSH KEY 和 GPG KEY]]></title>
            <link>https://maquanyi.com/articles/config-git-env-windows-11</link>
            <guid>https://maquanyi.com/articles/config-git-env-windows-11</guid>
            <pubDate>Thu, 04 Apr 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h3>安装 Git</h3>
<p>当然可以从 Git 的官方网站下载对应 Windows 版本进行安装，这里更推荐安装 Visual Studio Community 最新版本。 它自带了完整的 Git，不管开发 Rust 还是 NodeJS ，都需要一定的编译环境支持，索性一次到位比较好。 这个安装就不介绍了，如果这个都搞不定就用往下看了。</p>
<h3>安装 Git LFS</h3>
<p>现在 <a href="https://git-lfs.com">LFS</a> 基本成了标配，在官方网站下载对应版本安装后，记得执行 <code>git lfs install</code> 初始化。</p>
<h3>生成 SSH KEY 并加入到 SSH-AGENT</h3>
<pre class="language-bash"><code class="language-bash">ssh-keygen -t ed25519-sk -C <span class="token string">"your_email@example.com"</span>
</code></pre>
<p>这一步是最简单的，一路回车就行，生成的 Key 会保存在 <code>C:\Users\your_user_name\.ssh</code> 目录下。把生成的 <code>id_ed25519_sk.pub</code> 内容添加到 GitHub 的 SSH KEY 中就可以。如何能把 SSH KEY 加入到 <code>ssh-agnet</code> 是比较麻烦的事儿。试了几个方法，最后还是 <a href="https://github.com/ritazh/devopsfun/issues/17">SSH-ADD / Windows / Could not open a connection to your authentication agent</a> 提供的方法有效，</p>
<pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">cd</span> C:<span class="token punctuation">\</span>Program Files<span class="token punctuation">\</span>Git<span class="token punctuation">\</span>bin
<span class="token function">bash</span>
<span class="token builtin class-name">exec</span> ssh-agent <span class="token function">bash</span>
ssh-add ~/.ssh/id_ed25519
</code></pre>
<p>当然前提是你已经能够正常启动 <code>ssh-agent</code> 了， GitHub 官方给出的方案是创建一个 <code>.profile</code> 或 <code>.bashrc</code> 文件，这一步是在 Git bash 中执行, 它的路径是 <code>C:\Program Files\Git\bin</code> 。为了方便我把它加到了 Terminal 的配置中。</p>
<pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">env</span><span class="token operator">=~</span>/.ssh/agent.env

<span class="token function-name function">agent_load_env</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token builtin class-name">test</span> -f <span class="token string">"<span class="token variable">$env</span>"</span> <span class="token operator">&amp;&amp;</span> <span class="token builtin class-name">.</span> <span class="token string">"<span class="token variable">$env</span>"</span> <span class="token operator">&gt;|</span> /dev/null <span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token function-name function">agent_start</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token punctuation">(</span>umask 077<span class="token punctuation">;</span> ssh-agent <span class="token operator">&gt;|</span> <span class="token string">"<span class="token variable">$env</span>"</span><span class="token punctuation">)</span>
    <span class="token builtin class-name">.</span> <span class="token string">"<span class="token variable">$env</span>"</span> <span class="token operator">&gt;|</span> /dev/null <span class="token punctuation">;</span> <span class="token punctuation">}</span>

agent_load_env

<span class="token comment"># agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2=agent not running</span>
<span class="token assign-left variable">agent_run_state</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>ssh-add -l <span class="token operator">&gt;|</span> /dev/null <span class="token operator"><span class="token file-descriptor important">2</span>&gt;</span><span class="token file-descriptor important">&amp;1</span><span class="token punctuation">;</span> <span class="token builtin class-name">echo</span> $?<span class="token variable">)</span></span>

<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token operator">!</span> <span class="token string">"<span class="token environment constant">$SSH_AUTH_SOCK</span>"</span> <span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">[</span> <span class="token variable">$agent_run_state</span> <span class="token operator">=</span> <span class="token number">2</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
    agent_start
    ssh-add
<span class="token keyword">elif</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token environment constant">$SSH_AUTH_SOCK</span>"</span> <span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">[</span> <span class="token variable">$agent_run_state</span> <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
    ssh-add
<span class="token keyword">fi</span>

<span class="token builtin class-name">unset</span> <span class="token function">env</span>
</code></pre>
<p>如果这个启动脚本没有生效，可以尝试手动执行 <code>ssh-agent</code> 命令。</p>
<h3>生成 GPG KEY</h3>
<pre class="language-bash"><code class="language-bash">gpg --full-generate-key
</code></pre>
<p>在这一步根据提示输入你的姓名和邮箱，密码可以不输入，直接回车。生成的 Key 会保存在 <code>C:\Users\your_user_name\.gnupg</code> 目录下。 执行 <code>gpg --list-secret-keys --keyid-format=long</code> 查看生成的 Key ID，然后执行 <code>gpg --armor --export your_key_id</code> 导出 Key 内容，添加到 GitHub 的 GPG KEY 中。</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> config --global user.signingkey <span class="token operator">&lt;</span>Key ID<span class="token operator">&gt;</span>
<span class="token function">git</span> config --global commit.gpgsign <span class="token boolean">true</span>
</code></pre>
<p>上面的这个操作需要在 Git bash 中执行，而后每次提交代码的时候都使用 <code>git commit -s -S -m"commit message"</code> 这个命令， <code>-s -S</code> 参数表示使用 GPG 签名并添加 Signed-off-by 。</p>
<p>有时候在执行 GPG KEY 的时候长时间没有反应，卡在生成随机 Byte 的地方。</p>
<pre class="language-bash"><code class="language-bash">We need to generate a lot of random bytes. It is a good idea to perform
some other action <span class="token punctuation">(</span>type on the keyboard, move the mouse, utilize the
disks<span class="token punctuation">)</span> during the prime generation<span class="token punctuation">;</span> this gives the random number
generator a better chance to gain enough entropy.
</code></pre>
<p>这个界面无论如何动鼠标和键盘都没有反应， 感觉应该不是凑不够随机数的问题，应该是哪里有个 Bug ，但是在网上也没有搜到类似的情况给予说明和解决。</p>
<p>第一个解决办法是采用 SSH Key 对 Commit 进行签名，在配置文件中对 gpg 签名的 format 进行设置，测试是可行的。</p>
<pre class="language-ini"><code class="language-ini"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">user</span><span class="token punctuation">]</span></span>
	<span class="token key attr-name">name</span> <span class="token punctuation">=</span> <span class="token value attr-value">Eli Ma</span>
	<span class="token key attr-name">email</span> <span class="token punctuation">=</span> <span class="token value attr-value">eli@patch.sh</span>
	<span class="token key attr-name">signingkey</span> <span class="token punctuation">=</span> <span class="token value attr-value">C:/Users/meagl/.ssh/id_ed25519.pub</span>

<span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">commit</span><span class="token punctuation">]</span></span>
	<span class="token key attr-name">gpgsign</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span>

<span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">gpg</span><span class="token punctuation">]</span></span>
	<span class="token key attr-name">format</span> <span class="token punctuation">=</span> <span class="token value attr-value">ssh</span>
</code></pre>
<p>以上操作都是在 Git Bash 中操作的，所以使用的是 Git 安装目录下的 <code>gpg</code> 程序。 可以采用安装 <code>Gpg4win</code> 安装一个最新版本 <code>gpg</code> 程序尝试解决，但它生成的 Key 存储在 <code>C:\Users\your_user_name\AppData\Roaming\gnupg</code> ，这个 Key 无法被 Git 使用。 但是可以通过指定 git 的 <code>gpg</code> 程序路径的方式指定 <code>Gpg4win</code> 的 <code>gpg</code> 方法解决，需要修改配置文件。</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>filter <span class="token string">"lfs"</span><span class="token punctuation">]</span>
	clean <span class="token operator">=</span> git-lfs clean -- %f
	smudge <span class="token operator">=</span> git-lfs smudge -- %f
	process <span class="token operator">=</span> git-lfs filter-process
	required <span class="token operator">=</span> <span class="token boolean">true</span>

<span class="token punctuation">[</span>user<span class="token punctuation">]</span>
	name <span class="token operator">=</span> Eli Ma
	email <span class="token operator">=</span> eli@patch.sh
	signingkey <span class="token operator">=</span> AAAAAAAAAA

<span class="token punctuation">[</span>commit<span class="token punctuation">]</span>
	gpgsign <span class="token operator">=</span> <span class="token boolean">true</span>

<span class="token punctuation">[</span>gpg<span class="token punctuation">]</span>
	program <span class="token operator">=</span> C:<span class="token punctuation">\</span><span class="token punctuation">\</span>Program Files <span class="token punctuation">(</span>x86<span class="token punctuation">)</span><span class="token punctuation">\</span><span class="token punctuation">\</span>GnuPG<span class="token punctuation">\</span><span class="token punctuation">\</span>bin<span class="token punctuation">\</span><span class="token punctuation">\</span>gpg.exe

</code></pre>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Contributor License Agreement 浅析]]></title>
            <link>https://maquanyi.com/articles/contributor-license-agreement-cla</link>
            <guid>https://maquanyi.com/articles/contributor-license-agreement-cla</guid>
            <pubDate>Sat, 05 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>什么是 CLA，CLA 的主要作用</h2>
<p>在开源项目中通常存在三个角色围绕在整个项目的生命周期，在他们之间使用不同的协议约束之间的关系。</p>
<ol>
<li><strong>所有者</strong> 开源项目所有者的角色比较复杂，有可能是一个独立开发者个体，也可能是多个开发者组成的小团体或是一家商业公司。他们共同的特点是对这个开源项目具有控制权，拥有代码写入的权限和修改许可证的权利。</li>
<li><strong>贡献者</strong> 是指所有者之外的向开源项目贡献代码的开发者。有些开发者向开源项目贡献代码是出于个人意愿或者兴趣，我们可以称呼他们为独立贡献者，这些具有贡献精神的独立开发者是值得大家敬佩的，也是各种开源项目去努力吸引的；也有很多开发者是受雇于公司为开源项目贡献代码，大多数情况是因为工作中使用了某些开源项目，出于工作的需要贡献代码进行 Bug 的修复和新增功能，也有一些公司是有全职的开源开发者，专职在项目中贡献代码，这是一份让人非常羡慕的工作。</li>
<li><strong>用户</strong> 开源项目的用户是非常复杂的角色。用户可能是指使用在其它开源项目中使用这个开源项目的开发者，或是在商业项目开发中使用这个开源项目的开发者，也有可能是指使用这个开源项目开发的软件的用户，角色的切换随着场景在变化。</li>
</ol>
<p><strong>Open Source License</strong>，在中文中我们通常讲为开源许可证，是约束开源项目和用户之间的行为。开发者、用户或者是下游开源或商业项目是都有可能受开源项目的许可证约束，而且许可证的种类繁多，内容纷繁复杂，如果稍有不慎就会陷入法律纠纷的麻烦中。所以在目前开源合规方面的很多都是针对许可证合规的研究。</p>
<p><strong>CLA</strong> 是 <strong>Contributor License Agreement</strong> 的缩写，一般翻译为贡献者许可协议，开发者向开源项目贡献的时候通常被要求需要签署 CLA 协议(或类似协议)。CLA 是约束开源项目和贡献者之间的关系，通常是要求贡献者在代码和专利做出声明或者承诺。CLA 的协议内容一般都不长，通常包含两个部分：</p>
<h3>代码相关部分</h3>
<ul>
<li>签署 CLA 是要求贡献者声明对贡献内容(不仅限于代码，文档等其它内容一样有效)拥有所有权，或者是贡献者得到了相关的授权可以贡献这些内容。</li>
<li>贡献者卷提交的内容是在该开源项目选定的开源协议下进行，贡献的内容也遵循该项目的开源协议。</li>
</ul>
<h3>专利相关部分</h3>
<ul>
<li>签署 CLA 是要求贡献者声明如果贡献中包含了专利，该贡献者拥有该专利的所有权，或者得到相应的授权可以贡献该专利。</li>
<li>签署 CLA 等于贡献者授权开源项目以及开源项目的用户能够使用这个专利</li>
</ul>
<p>CLA 对于开源项目的贡献者和用户也是一种保护，能够帮助开源项目避免不必要的诉讼和骚扰。</p>
<ul>
<li>贡献者签署 CLA 后是对贡献者的一种保护，签署 CLA 并不能改变捐献的代码和专利的所有权，只是授权的一种行为。</li>
<li>因为开源项目使用了 CLA ，每一个贡献都在 CLA 的保护下，在出现侵权的诉讼时，用户或开源项目可以引用 CLA 作为自己免责的证据。</li>
</ul>
<p>所以，针对对 CLA 进行一个简要的总结：</p>
<blockquote>
<p>Contributor License Agreement 是一个轻量级的许可协议，签署人是代码(包含文档等其它内容)和专利的拥有者(或是被授权贡献)，授权这些贡献在项目的开源许可证下进行再分发。</p>
</blockquote>
<h2>CLA 和 DCO 之间的区别</h2>
<p>DCO 的全称是 Developer Certificate of Origin ，是贡献者声明贡献内容所有权的轻量级协议，这里可以把 DCO 看做是更轻量级的 CLA 。CLA 和 DCO 的区别：</p>
<ol>
<li>CLA 签署一般是在贡献前签署，签署一次后后续贡献就不用再签署；DCO 是需要在每次提交代码时签署。如果使用 git 作为版本管理工具，在签署时添加 -s 参数可自动完成签署，签署后会在 Git Message 的尾部添加&nbsp;Signed-off-by: Random J Developer <a href="mailto:random@developer.example.org">random@developer.example.org</a>&nbsp;的内容，姓名和密码来自于 Git 的 Config 文件。</li>
<li>CLA 通常包括一个管理系统，主要是为签署 CLA 的企业管理向开源项目贡献的员工。在管理系统中通常是通过贡献人的邮件地址或者 ID (版本管理服务的 ID，类似 Github.com ) 识别是否企业员工，通过集成到 PR (Pull Request) 来控制代码合入；企业员工更换工作内容或者离职后，企业可以通过管理系统取消其代表企业的贡献权限。openEuler 操作系统开源社区采用的 CLA 系统就是社区根据实际情况自研的，可以在 <a href="https://github.com/opensourceways/app-cla-server">https://github.com/opensourceways/app-cla-server</a> 获取到代码。</li>
</ol>
<blockquote>
<p>CLA 的签署过程可以看做是显示签署，CLA 的内容可以进行修改，通常大型企业主导的开源项目会使用 CLA 的模式，这样更容易地控制法律文本内容，降低诉讼产生的风险；目前 DCO 的内容主要是来自 <a href="https://developercertificate.org">https://developercertificate.org</a> 中，在 Github 中使用 DCO 的应用检查是否含有 Signed-off-by 时默认认可 DCO 的内容，是隐式签署，企业也无法控制 DCO 的文本内容。</p>
</blockquote>
<h2>开源社区的一些正面和负面的看法</h2>
<p>目前 Google 、Microsoft 、华为等大型企业的开源项目都使用了 CLA ，减少了针对开源项目的诉讼风险，利于开源项目的发展。但是社区对 CLA 也存在一些负面的看法：</p>
<ol>
<li>签署 CLA 等于放弃了一些法律权利，但是羞涩的法律文本对于开发者来说都是不友好的，即使认真地阅读了 CLA 内容，也很难确定到底是要放弃什么样的权利。很多开发者在第一次贡献前，就可能因为要签署 CLA 而放弃了。</li>
<li>还有一些开发者认为 CLA 更多是为企业贡献者服务，对于个人开发者并不友好，并没有考虑到他们的感受和贡献的意愿；甚至有一些开发者认为这些签署 CLA 的项目并不是把开发者放到第一位考虑，而是先努力降低自己的风险。</li>
<li>最近几年几个著名的开源项目更改 License 协议，也带来了开发者对签署 CLA 的抱怨。有很多开发者认为开源项目没有权利更改 License 协议，或者是滥用了签署 CLA 赋予的权利。</li>
</ol>
<p>总之，对于开源项目的所有者或背后实际控制的公司，都倾向于使用 CLA 的方式代替 DCO 来获取贡献者的授权，相信未来越来越多的开发者在参与开源社区贡献时，需要签署 CLA。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[《开发者关系 - 方法与实践》读书笔记 - 译者序之得开发者得天下]]></title>
            <link>https://maquanyi.com/articles/developer-relations-1</link>
            <guid>https://maquanyi.com/articles/developer-relations-1</guid>
            <pubDate>Sun, 09 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Richard 寄来签名版赠书已经有一段时间，从 4 月份去阿姆斯特丹参加 <strong>KubeCon</strong> 开始就突然忙碌起来，根本没有时间和心情打开阅读任何文字，连上网看新闻都少了很多。虽然在 5 月参加 GOTC 会议的时候和刚刚离职的 Richard 吃了两顿饭，但是依旧无法平静心情翻开仔细阅读。昨天在深圳打卡了一个下午讲两个不同的议题，连续差旅的忙碌至少告一段落，终于在回程的飞机上开始翻看。</p>
<p>我有一个非常不好的习惯，看书的时候先看各种序、读一下文章目录，每章看个开头，然后就扔在那里等什么时候需要的时候再去翻。所以再大部头的书到我手里也是分分钟的就扫了一遍，扔到书架上去吃灰。 这次下定决心把每个段落都认真的读一下，通过笔记的方式强迫自己把思考的内容记录下来。另外也是正用 Rust 开发的新版本管理系统，这是一个必须依赖开发者生态的开源项目，如何针对这个特别的人群做好产品和服务成为最近思考的焦点，也算是强迫认真读这本书的一个动力。</p>
<blockquote>
<p><em>开发者生态已经是很多新形态商业模式的核心价值和关键要素。它是商业模式的护城河，是平台经济的磐石基座，是伙伴拓展的助推动力，是软件供应链韧性和创新性的双重保障。换句话说，一旦生态在发展过程中过了规模拐点，就会形成事实上的垄断。即可为生态的开局庄家和早期的入局玩家带来超额利润，直到新的技术、商品及应用场景垫付原来的商业模式。 - <strong>《开发者关系 - 方法与实践》序 - 得开发者得天下</strong></em></p>
</blockquote>
<p>“<strong>新形态商业模式</strong>”，我猜 Richard 这里讲的是 “<strong>以开源为模式的商业形态</strong>”，这真的是一个当前又热又大的话题。还没有哪本书或者哪个人把 “<strong>以开源为模式的商业形态</strong>”定义清楚或者让大多数人信服，唯一可以笃定的是开发者关系是其中关键的一环。在深圳参会拿到了李建盛签名的 《开源之迷》，翻了翻目录好像也没有看到对这个热点的分析。当然我也不是那个能讲明白这个事情的人，还是留做以后读书的时候慢慢思考。前两周也在和一个朋友提到应该搞一个 “开源经济学” 的研究，通过经济学的研究方法来看待开源对世界变化产生的影响。这是我非常感兴趣的方向，但没有时间投入做学术研究。当然内心对这个方向的观点其实已经成形：</p>
<blockquote>
<p><strong>::开源是经济全球化（Economic Globalization）和信息技术发展交织下的必然产物，随着地缘政治和去全球化的浪潮，开源必然会被国家力量所控制成为政治、经济和军事角逐的棋子，未来会诞生一股 “地下”开源的全新方向。::</strong></p>
</blockquote>
<p>早晚有一天使用开源软件就跟收听 “敌台” 一样，我猜很多人心里都知道会有这样一天的到来…</p>
<p>“<strong>生态的开局玩家</strong>” 和 “<strong>早期的入局玩家</strong>” 这两个词用的非常巧妙，让我顿时想起若干年前玩的星际争霸游戏。<strong>“开局”</strong> 和 <strong>“入局”</strong> 两个词把开源早期的两个非常难的关键动作形容的惟妙惟肖。</p>
<p>“<strong>开局</strong>” 是一个开源项目早期阶段，如何获得核心开发者的认可，在形成开发者口碑的同时获得一两个商业种子用户，是我认为开局的 “<strong>KPI</strong>” 。很多开源项目（尤其是背后是商业公司的开源项目）把 GitHub 的 Star 数量作为衡量指标。有些人归咎于大多数管理层既不懂社区也不懂开发者的仓促决策，这种现象从根上讲是没有把自己作为一个开源项目的位置摆正，不知道自己是什么产品的一部分，还是一个什么 xxx 生态战略的一部分。当然我也听过某厂的 “千秋万代、一桶（统）江湖” 的战略，如果是这个战略的一部分不提桶跑路还等什么，等着领导送一本 “葵花宝典” 么？ 当然也有一些大厂把开源项目当做是对开发者的普世恩惠，然后还像防贼一样防着开发者，既想绑定开发者还怕开发者反水，全天忽悠着 “开源开放还反分裂”，我觉得这样的项目就挺精神 “分裂”，所以跑路还是要趁早。</p>
<p>找到 <strong>“早期的入局玩家”</strong> 也是比较难的事情，难在缺少这样的复合型人才开拓这个领域。这些年看身边没有找到一个符合这样模型的人，一个 “<strong>相关领域技术专家、有开发者资源和商业资源的技术型 BD</strong> ”，当然也有公司把这个事儿拆成三个职位，搞一个 “铁三角” 的组织模型来搞。理想很伟大，现实很骨感，能找到三个人还能一条心干到一起，也是个天方夜谭。</p>
<blockquote>
<p><em>开发者既是软件产品服务的消费者，也是创作者，同时还是价值创造者。 - 《开发者关系 - 方法与实践》序 - 得开发者得天下</em></p>
</blockquote>
<p>Richard 职业生涯的很多时间都在和开发者打交道，这是他能深刻理解开发者价值的原因。要放弃对大厂那些开发者产品，尤其是一些云厂商的开发者产品，他们产品的多数都是为了绑定开发者，让其消耗云资源而获利。</p>
<hr>
<p>让我们对那些 “<em>3 年超越 Gitee、5 年超越 GitHub</em> ”的鬼话一笑而过吧😄😄😄</p>
<blockquote>
<p>文章评论，请到 <a href="https://www.craft.me/s/Jfc2h9foQWHrJP">Craft 链接</a></p>
</blockquote>
<h3>《开发者关系 - 方法与实践》读书笔记汇总：</h3>
<ol>
<li><a href="/articles/developer-relations-1">《开发者关系 - 方法与实践》读书笔记 - 译者序之得开发者得天下</a></li>
<li><a href="/articles/developer-relations-2">《开发者关系 - 方法与实践》读书笔记 - 推荐序 2 - 5</a></li>
<li><a href="/articles/developer-relations-3">《开发者关系 - 方法与实践》读书笔记 - 第一部分形成广泛的共识</a></li>
<li><a href="/articles/developer-relations-4">《开发者关系 - 方法与实践》读书笔记 - 第一部分开发者关系的定位</a></li>
</ol>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[《开发者关系 - 方法与实践》读书笔记 - 推荐序 2 - 5]]></title>
            <link>https://maquanyi.com/articles/developer-relations-2</link>
            <guid>https://maquanyi.com/articles/developer-relations-2</guid>
            <pubDate>Sat, 22 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>还是像上一篇序一样，所有的阅读、思考和文字都是在移动的过程中产生。在北京飞深圳的差旅起点读完序 2 - 5 并做完笔记，在差旅的最后一站长沙将文字敲到电脑上，通过 <a href="https://maquanyi.com">博客</a> 和<strong>云原生喝酒 SIG 酒委会主席老崔</strong>的公众号 “<strong>伪架构师</strong>” 发出来。</p>
<p>推荐序 2 到 推荐序 5 都有一个共同特点，没有文章名。Richard 写的译者序名字是《得开发者得天下》，多写几篇类似的文字我觉得就能组织一本什么散文集出版了。我觉得这个应该是出版社编辑的问题，请人写序一定要让人家写个题目，要不序就写的更敷衍了。</p>
<hr>
<h3>推荐序 2</h3>
<p>推荐序 2 是由微软(中国)有限公司创新技术总经理崔宏禹写的。怎么说呢，通篇文字写的都没错，但是也没有输出什么观点。我严重怀疑这篇文字被微软的律师改过 90% 以上，字字正确严丝合缝不漏任何风险，符合大公司执业律师的特点。</p>
<h3>推荐序 3</h3>
<p>推荐序 3 是由亚马逊云科技首席架构师费良宏编写的，里面有些文字让我有些感触。</p>
<blockquote>
<p><em>但公认的一点是，微软公司和苹果公司都以 “开发者关系” 而闻名，两者都是世界知名的企业，在成为世界知名企业之前，它们就专注于与开发者建立关系。据说，微软公司每卖出一个 Visual Studio （Windows 集成开发环境工具）的许可证，就会额外增加 5 个 Windows 许可证的销售。</em></p>
</blockquote>
<p>在微软和苹果公司的同时期，我觉得还有一家注重开发者关系的伟大（但倒闭）了公司，就是 Borland 。绝大多数 70 后使用 Delphi 的工程师都对这家公司有深厚的感情，至今我还依旧订阅了 Embarcadero 的产品邮件，看到它依旧还存在的消息略感欣慰。</p>
<p>Borland 的没落是平台型产品公司之间 “互卷” 的结果，从技术和产品的角度看，我觉得有两点可以深入思考。从技术角度谁离底层越近，谁就更有竞争优势， .Net 战略如果不是微软主导，而是其它的公司话 Borland 也不见得能输，而且输的这么彻底。从产品角度来讲，如果底层技术不能把握，上层产品就是口吐莲花也只能在表层追求所谓的开发者体验，产品越大就越捉襟见肘。我觉得这也是很多开源项目都会在核心的上游贡献特性，这也是为了能植入一些技术点从而成就自己的产品。</p>
<blockquote>
<p><em>“在开发者社区中，我代表公司；在公司中，我代表开发者社区。我们必须时刻考虑到开发者的利益。”</em></p>
</blockquote>
<p>当前开发者社区和开源社区在国内已经没有了界限的划分，逐渐模糊化，甚至默认开发者社区就是一个开源社区，从我的认知中这两者还是有明显的区别。当然开源不开源是一个界限，还有一个就是关系网络模型。一个是伞状社区而另一个是网状社区，我觉得作者这句话在当前国内的环境下不是普世的做法，也由于这个开发者关系在国内的建立是非常难的，一个疯狂内卷的环境下建立一个开发者的网络结构是非常痛苦。</p>
<blockquote>
<p><em>为什么开发者关系很重要？</em></p>
</blockquote>
<p>看到作者的这个小标题和内容后，我觉得文字内容并没有解释这个标题。</p>
<blockquote>
<p><em>为什么开发者关系是有效的？</em></p>
</blockquote>
<blockquote>
<p><em>这是因为开发者关系强调与开发者建立信任的关系。当开发者选择某种技术产品或服务时，这种支持与信任会持续很多年。</em></p>
</blockquote>
<p>作者在文章点了两个非常重要的题，第二个题解的就好了很多。这个信任关系是如何建立的呢，这个链路中是有一个必不可少的 “产品”或“服务”。这个也是我觉得一个社区要提供给开发者一个价值，当然我更认可 “产品” 价值，这也是以前我演讲时候经常说的，社区是以共同兴趣为核心构建的。当然 “服务” 也是可以的，但是这样的 “服务” 我还没有见到成功组成社区，并形成信任关系的案例。</p>
<blockquote>
<p><em>许多开发者都有博客和社交媒体账户，通过他们的宣传可以将这种信任放大，最终达到通过一名开发者影响更多开发者的效果</em></p>
</blockquote>
<p>我想把这个总结为 “开发者的网络效应” ，但是如何完整定义这样的 “网络效应” 一时我也想不出一个不错的说法，还是把这个总结留给  <strong>ChatGPT</strong> ，没事的时候就可以和它深入讨论一下。</p>
<blockquote>
<p><em>在实践中，我认为开发者关系是 3 个学科的交叉点 - 工程、营销和社区管理。而开发者关系这项工作的三大支柱则是代码、内容和社区。</em></p>
</blockquote>
<p>这个交叉学科的讲法非常有意思，但是我感觉不止 3 个学科的交叉点这么简单。我更倾向于把这个解释为经济学（怎么样，是不是更忽悠），这也是最近在和一些同事讨论，如何把 “开源经济学” 作为一个学术方向来研究。</p>
<h3>推荐序 4</h3>
<p>推荐序 4 是由 CSDN 的蒋涛撰写，怎么说感受呢，就一个词 “<strong>敷衍</strong>” 。其中有一句话是 “过去在中国开发者关系和生态一直不受重视，近年来随着中国开源技术相关产品的蓬勃发展，开发者关系和社区建设变得越来越重要”，我读完这句的感受是，<strong>“随着社会经济和互联网的发展与进步，开发者群体从屌丝突然变成会下金蛋的老母鸡，这些人变成了韭菜，可以揠苗助长，杀鸡取卵。开不开源的无所谓，能赶上国家的政策，割韭菜的时候套点钱也不错“。</strong></p>
<h3>推荐序 5</h3>
<p>推荐序 5 是由 InfoQ 的霍泰稳编写，用两个字来形容就是 “<strong>硬广</strong>” ，在商言商我觉得也没毛病。</p>
<hr>
<p>这几天我看完感觉是可以把 <em><strong>“开源经济学”</strong></em> 和 <em><strong>“开发者网络效应”</strong></em> 这些词组织起来写一篇论文来论述一下，当然这不是我这样生物系毕业的学渣能做的事情，希望有缘人能把这些东西变成一个可以讲的故事。预告一下我现在思考的很可怕的两个词 <em><strong>“匿名开源”</strong></em> 和 <em><strong>“匿名开源组织” …</strong></em></p>
<blockquote>
<p>文章评论请到 - <a href="https://www.craft.me/s/VC0artatd9e08M">Craft 链接</a></p>
</blockquote>
<h3>《开发者关系 - 方法与实践》读书笔记汇总：</h3>
<ol>
<li><a href="/articles/developer-relations-1">《开发者关系 - 方法与实践》读书笔记 - 译者序之得开发者得天下</a></li>
<li><a href="/articles/developer-relations-2">《开发者关系 - 方法与实践》读书笔记 - 推荐序 2 - 5</a></li>
<li><a href="/articles/developer-relations-3">《开发者关系 - 方法与实践》读书笔记 - 第一部分形成广泛的共识</a></li>
<li><a href="/articles/developer-relations-4">《开发者关系 - 方法与实践》读书笔记 - 第一部分开发者关系的定位</a></li>
</ol>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[《开发者关系 - 方法与实践》读书笔记 - 第一部分形成广泛的共识]]></title>
            <link>https://maquanyi.com/articles/developer-relations-3</link>
            <guid>https://maquanyi.com/articles/developer-relations-3</guid>
            <pubDate>Fri, 28 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>第二篇读书笔记贴到 Facebook 账号后，有朋友来问怎么都是序，我承认读书太慢，这次出差到南京的火车上再次拿起随行的这本书，把第一部分形成广泛的共识的第1章开发者关系基本概念读完了，现在感觉这本书不是一本讲道理的书，而是一本给项目经理的手册。</p>
<hr>
<blockquote>
<p><em>在启动开发者关系项目之前，相关人员有必要对开发者关系形成广泛的公司和。这里的“相关人员”指的是开发者关系的从业人员和开发者关系业务的利益相关者，其中不仅包括企业的最高管理层，如首席执行官和董事会成员，也包括所有涉及开发者关系活动的部门（如首席技术官办公室、市场部门、产品部门、客户服务部门等）的员工，还包括公司外部的团队（如营销机构、公关公司或承包商）的成员。</em></p>
</blockquote>
<p>看完这第一段让我有点不知所以然，这么重要的话题不是应该更深层次的讨论，达成广泛共识的基础是什么？翻了翻这一部分的其余 4 章，并没有对达成共识基础的内容。第 4 章开发者的经济价值看上去是讲开发者能产生的价值，对于形成共识并不具有指导意义。</p>
<p>在商言商，一个公司的核心利益是为股东服务的，说直白点就是为了“赚钱”，所以一个公司讲自己不赚钱而做什么样什么样的开源，贡献社会共建社区，千万别信这就是骗局。我的观点是，经济价值的体现，是公共你是内形成广泛共识的基础。一个产品如果通过开发者关系获得利益，也就得到了这个产品团队、研发团队的共识，不管这个利益是有形资产还是无形价值。一个主要面向 2B 国产新创的操作系统，你说让它变成一个所有开发者都使用的操作系统，带来新的收益者明显是和产品价值逻辑不相符的，所以不会得到相关产品团队和研发团队的共识。即使采用“硬广”砸钱的方式宣传，也改变不了它不是为开发者服务的根本，其所谓的看到这关系不过是公司政治层面的一朵绣花而已。共识的基础是共同的价值，不能带来价值就没有共识。</p>
<p>在前言中有一些文字，也带来对共识的思考，就是谁对开发者关系而负责？</p>
<blockquote>
<p><em>开发者关系（Developer Relationship，DevRel）的定义在行业内和行业外均存在着分歧，这反应了开发者关系的复杂性。事实上，每一家已有或计划开展开发者关系项目的公司，都应任命一位与 CTO、CIO 同级的开发者关系负责人（Chief Developer Relation Officer，CDRO）。只有开发者关系负责人在企业中为开发者发声，才能确保开发者关系部门的工作与公司核心的战略部门保持一致。</em></p>
</blockquote>
<p>这些文字的背面透露着作者也不愿说的事实，其实在当前的国内外企业中，都没有定义清楚到底谁为开发者关系而负责，引申的就是开发者关系价值没有被接受，负责开发者关系的预算也无从着落。这一系列尴尬的背后，说明开发者的价值还不被主流经济模式接受。从我的观点，产品是开发者关系的最终受益者，每一个产品经理都应该具有相关的意识，这是产品设计的核心逻辑。在之前的一次演讲中介绍过我对 <a href="https://github.com/tkestack">TKEStack</a> 产品设计的逻辑，大家可以在<a href="https://www.bilibili.com/video/BV1m14y1N71t/"> B  站</a>看到我的表述。如果有哪个公司在找 CDRO ，真心推荐我的朋友，也是这本书的译者林旅强，可以找我要他的联系方式，请用推荐费请我喝啤酒 😂 。</p>
<hr>
<blockquote>
<p><em>你可以通过一对一的交流加你共识，但更好的做法是在开发者关系项目启动会上，让所有利益相关者聚在一起，充分沟通，发现并解决跨部门的问题和争议，最终达成一致。</em></p>
</blockquote>
<p>这个我就完全不认同了，在国内公司的传统做法就是学习外企搞个项目启动会，共识不共识的不是关键，关键是要请一位高层领导来展台，压住下面各个心怀鬼胎的利益方在表面达成一致。我更喜欢一对一的交流，通过信息不对称形成一个小闭环，让事情进展下去，达成一致这种事儿是不可能的，还是让幻想早点破灭吧 😂 。</p>
<blockquote>
<p><em>事实上，开发者关系团队需要投入足够的时间和精力，比如召开项目例会、定期汇报进度，确保重要利益相关者了解开发者关系项目的进展，并持续提供支持。</em></p>
</blockquote>
<p>这个翻译一下就是因为价值归属不明确，必须经常去高层汇报，从高层获得支持持续向各利益方施压维持，<em><strong>这真是一个又累又卷又不讨好的工作</strong></em>。</p>
<hr>
<p><em><strong>以下是第 1 章开发者关系基本概念阅读时的一些记录。</strong></em></p>
<blockquote>
<p><em>事实上，开发者关系至今尚未成为高等或专科教育的一门专业，也没有形成类似开发者关系协会的专业性组织。</em></p>
</blockquote>
<p>国内一些开发者媒体在当前充当着这样的橘色，有总比没有好。但是这样的媒体属性，必然是偏向利益驱动的，专业性就打折扣了。</p>
<blockquote>
<p><em>开发者关系包括两个方面：在企业外部，想开发者推广技术产品；在企业内部，促进开发者关系项目取得成功。</em></p>
</blockquote>
<p>其实这就是我执意这本书是一个手册的原因，连共识是什么都没有观点，怎么能定义成功呢？ 现在的开发者关系全屏公司高层对这个事儿的理解，所以才有前文要搞启动会、例会和汇报等内容。如果不仔细分析，还以为作者在某某公司工作过，某某是哪家公司请勿对号入座 😂 。</p>
<blockquote>
<p><em>开发者体验通常所写为 DX（Developer Experience），位与树冠的中心，它是任何开发者关系的核心。</em></p>
</blockquote>
<p>国内的产品逻辑是让管理者更好的管理开发者，让他们朝九晚五，产品的核心价值用户是管理层。因为管理层是能决定买单的人，所以国内产品的逻辑不是解决开发者的问题，是解决提出问题的开发者。</p>
<blockquote>
<p><em>开发者培训（Developer Eduction）对开发者采用平台技术至关重要。</em></p>
</blockquote>
<p>这个是好事儿，但是经常演变为卖证书。开发者为什么要买证书？因为招标文件上有证书数量要求。为什么招标文件上有证书数量要求？问太多了就不好了。要问还是问为什么开源还要定标准这事儿吧。</p>
<blockquote>
<p><em>正如树木的成长离不开树干和树根，成功的开发者关系项目离不开活跃的开发者社区（Community）。整个嗯开发者关系项目的核心目标就是要融入、服务并且培育你的开发者社区。</em></p>
</blockquote>
<p>这里的问题来了，这里 ”你的社区“ 到底是谁的社区。从公司角度来讲肯定是属于公司的社区，社区的人需要接受公司的管理和领导，社区不容许分裂等等。如果这是一个开源社区呢，上面的逻辑还成立么？</p>
<hr>
<p><em><strong>共识在一家商业公司中是非常难，如果没有乔帮主那样的大神镇场子，Apple 不会成为现在这样的公司，每个公司都应该有一个灵魂人物，这个人是影响到公司对开发者关系的认知。 开源社区也是一种精英社区文化，少数人领导多数人。所以开发者关系在国内的推动我觉得一两本书是不可能让那些“商业领袖”认识到价值，国内需要一个强开发者关系的伟大产品和其精神领袖才能让这个相对封闭的体系认可生态带来的可能性。</strong></em></p>
<blockquote>
<p>文章评论，请到 <a href="https://www.craft.me/s/Tuaifz9LRu3qLp">Craft 链接</a></p>
</blockquote>
<h3>《开发者关系 - 方法与实践》读书笔记汇总：</h3>
<ol>
<li><a href="/articles/developer-relations-1">《开发者关系 - 方法与实践》读书笔记 - 译者序之得开发者得天下</a></li>
<li><a href="/articles/developer-relations-2">《开发者关系 - 方法与实践》读书笔记 - 推荐序 2 - 5</a></li>
<li><a href="/articles/developer-relations-3">《开发者关系 - 方法与实践》读书笔记 - 第一部分形成广泛的共识</a></li>
<li><a href="/articles/developer-relations-4">《开发者关系 - 方法与实践》读书笔记 - 第一部分开发者关系的定位</a></li>
</ol>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[《开发者关系 - 方法与实践》读书笔记 - 第一部分开发者关系的定位]]></title>
            <link>https://maquanyi.com/articles/developer-relations-4</link>
            <guid>https://maquanyi.com/articles/developer-relations-4</guid>
            <pubDate>Mon, 14 Aug 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>最近没有写读书笔记的主要原因是在读另一本书《排名诡计》，只是读了一部分就让我对当前的各种排行榜和排名有了一个新的认知，推荐大家去翻一翻。上一篇读书笔记在老崔的公众号发布后，他截屏发到群里面竟然有人在催更，这比公众号广告挣到 0.7 元分成更让我震惊，真的有人在读这些无聊的东西么？ 和老崔交流后的总结是 “<em><strong>中年人最健康的变态方式就是抽烟喝酒骂街</strong></em>“。</p>
<hr>
<p>这次竟然不是在差旅的途中读书，着实是一个好习惯的开始。</p>
<p>这次阅读的是第一部分第 2 章 - 开发者关系的定位，通读下来觉得这里所说的开发者关系定位，只是指在一个企业中开发者关系的业务归属，汇报层级和部门之间合作的方式等等。</p>
<blockquote>
<p><em>产品部门通常将关注点放在消费者需求上，以提供出色的产品体验为目标。一般而言，从产品走进市场的那一刻开始就以为着产品部门的使命已经达成。但开发者关系并非如此。</em></p>
</blockquote>
<p>就像之前说的，这本书开始并没有把所说企业情况讲清楚，猜测作者的角度默认是一个非 IT 类的产品研发型企业，即使如此我也十分不同意产品上市就是产品部门的使命完成。这不是应该产品部门（经理）端到端（潮流点的词叫 E2E ）负责，直到产品不再生产下市么？我理解产品全生命周期都应该是产品部门负责，或者有产品经理从始至终的跟到结束。开发者关系的周期应该和产品的生命周期绑定在一起，生命周期我觉得应该稍长于产品的生命周期，或者延续到下一个接替的产品。</p>
<p>如果是我写这本书，我可能会把和开发者关系有关的企业做个分类，通过不同分类分析不同的开发者关系定位，譬如 SaaS、PaaS、IaaS、小程序等等吧。另外开发者关系的定位，我想应该是分析不同类型产品和开发者关系的关系，关注背后的商业逻辑。所以书中的 “<em><strong>开发者关系活动的背后涉及许多商业行为，仅仅关注产品是远远不够的</strong></em>” 这句我觉得是对的，只是在一个公司内如何定位，我个人认为完全取决于领导层对这个事情的认知。也有些人喜欢讲什么要教育领导层如何认知开源或开发者关系，要向上管理云云，至少在中国企业文化中，这完全是痴人说梦。哪个公司的领导如果能把这个事儿放在台面上，都不用说放在心上，中国早就会出现一些受人尊敬的软件公司了。</p>
<blockquote>
<p><em>市场营销部门通常认为开发者关系和传统的 B2B（Business-to-Business， 企业对企业）/ B2C （Business-to-Consumer，企业对消费者）营销部门活动非常类似。他们认为，传统的<strong>市场营销方法</strong>同样适用于开发者关系。但实际上，开发者是一类特殊的消费者群体，需要用全新的视角去了解。</em></p>
</blockquote>
<p>所以国内很多公司，关于开源运营工作大部分是由 Marketing 部门来负责，采用的手段就比较传统。买渠道扩大宣传，搞案例做行业会议，请院士请政府领导站台，胁迫合作伙伴造势，这一套在涉及现在所谓“信创”相关的国产开源项目是相当的好使，只要砸钱够多，就能营造出一个开源项目生态蓬勃的景象。不知道请个院士出场的车马费是多少钱，开大会拼院士已经是个 Fashion 了，至于谁是开发者，到底是谁在用反正 Marketing 出个报告就行，搞个白皮书几十万花的相当值。当然这个策略在公司角度是没啥问题，因为国内真正付钱的甲方采购不看生态不看功能，看的是政策导向和采购要求。只是在这个循环中根本没有开发者，这也就是国内开源开发者关系的现状吧。没有哪个老板相信一个所谓开发者会议有上亿人观看直播，但是这个事儿也不能戳破，大家还是要一起讲故事。不用追去到底是什么 “全新” 视角，至少是一个还有开发者存在视角去了解开发者关系的定位吧。</p>
<blockquote>
<p><em><strong>社区运营部门</strong>通常认为开发者关系的工作 … 都是帮助对方解决问题，发挥他们的创造性，而不是让他们觉得自己是一个需要达成的业绩指标。</em></p>
</blockquote>
<p>不知道国内公司有没有这样的运营部门，我猜大多数运营指标就是所谓社区有多少开发者，社区就是个论坛，没有论坛也要建个论坛，只要来注册的开发者就属于这个社区。管理者把 “拉新促活” 挂在嘴边，当然还有想把营收指标作为社区运营部门的 KPI 。这其实也不算懒政，是老板根本不认为社区运营部门有价值，在头上悬一个 “达摩克利斯之剑”，啥时候砍头祭旗就看老板啥时候需要借机整理职场。</p>
<hr>
<p>这一章有一节是讲 <strong>开发者关系的汇报结构</strong> ，内容上找不到什么有价值的文字，这里只说一下我观察到的国内开源开发者关系的汇报结构现状。</p>
<ol>
<li>有一种最简单的管理方式是<strong>产品团队有专人负责开发者关系</strong>，向产品团队的领导进行汇报。这种模式多见于创业公司，创业项目多是一个领域内的解决方案，开发者关系相对比较好，是他们开源项目的用户。</li>
<li>有一种比较松散的管理方式是在<strong>公司内成立开源委员会</strong>，这样公司的一些共性是公司的管理层中有 1 ～2 位级别比较高的领导对于开源是认可的，但是还找不到开源和公司业务结合的最佳方式，希望通过一个松散的开源委员会让公司的开发环境相对开放。委员会汇报给对于认可开源的高层领导，是一个融洽的管理关系。但真正的开发者关系工作如何在开源项目中得到重视和推动，一个委员会是远远不够。</li>
<li>有一种比较复杂的管理方式是<strong>成立 OSPO （Open Source Program Office）</strong> 。如果公司的 OSPO 只管理内部使用开源，这个公司多半于担心对外贸易的过程中是否会受到合规威胁，希望通过 OSPO 来提升内部使用开源的合规能力提升产品的竞争力；如果这个公司 OSPO 同时管理外部开源，那这个公司多半是有个什么矩阵管理体系，OSPO 的工作处在复杂的内部利益斗争中，可能都自身难保，更何况开发者关系什么定位了，工作也就只剩下汇报这件事儿了。</li>
</ol>
<hr>
<p>这章最后的 “<strong>开发者关系测试</strong>” 我觉得是非常有用，用来判断是否和开发者关系的工作有关：</p>
<blockquote>
<p><em><strong>我是否在开发者关系关系的领域工作？</strong></em></p>
</blockquote>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->&gt; 你是否以开发者为主要的目标受众？</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->&gt; 你的战略和战术是否旨在影响开发者的行为？</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->&gt; 你的成果是否取决于开发者？</li>
</ul>
<hr>
<p><strong>开发者关系的定位，应该思考的是开发者和公司商业价值的关系，而不是汇报的关系。当然手册还是要当手册来读，这手册后面多半也不讲如何实现价值。目前看前两章可以形容为外企开发者关系厚黑学手册，其实实话实说就好了，老外就是喜欢玩政治正确。</strong></p>
<blockquote>
<p>文章评论，请到 <a href="https://www.craft.me/s/bkCRa1GZhOIRnG">Craft 链接</a></p>
</blockquote>
<h3>《开发者关系 - 方法与实践》读书笔记汇总：</h3>
<ol>
<li><a href="/articles/developer-relations-1">《开发者关系 - 方法与实践》读书笔记 - 译者序之得开发者得天下</a></li>
<li><a href="/articles/developer-relations-2">《开发者关系 - 方法与实践》读书笔记 - 推荐序 2 - 5</a></li>
<li><a href="/articles/developer-relations-3">《开发者关系 - 方法与实践》读书笔记 - 第一部分形成广泛的共识</a></li>
<li><a href="/articles/developer-relations-4">《开发者关系 - 方法与实践》读书笔记 - 第一部分开发者关系的定位</a></li>
</ol>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[DevOps Is Philosophy]]></title>
            <link>https://maquanyi.com/articles/devops-philosophy</link>
            <guid>https://maquanyi.com/articles/devops-philosophy</guid>
            <pubDate>Thu, 03 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>DevOps is an operational philosophy that promotes better communication between development and operations as more elements of operations become programmable.</p>
<p>DevOps is philosophy, and it's invisible. But it is like the soul through our work, either development, operation, testing even marketing or business. There are no rules in DevOps, no definition in DevOps and no right or wrong in DevOps. You would do DevOps in your way, I do it in my way.</p>
<p>You would do DevOps in your way. I do it in my way.</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[EuroRust 2023 -  从北京到布鲁塞尔]]></title>
            <link>https://maquanyi.com/articles/euro-rust-2023-trip</link>
            <guid>https://maquanyi.com/articles/euro-rust-2023-trip</guid>
            <pubDate>Tue, 10 Oct 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="北京出发" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbeijing.de632abf.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbeijing.de632abf.jpg&amp;w=3840&amp;q=75">
<p>从北京出发的时候天气秋高气爽，走六环到首都机场还是一切顺利没有堵车，这是第一次从机场北面进入 T3 航站楼，看到机场后面很多货运的停机坪和硕大的仓库。从首都机场国际出发的人并不多， 很多免税店还没有开，不知道是疫情的冲击没有恢复，还是被大兴机场分流严造成客流减少。</p>
<img alt="" loading="lazy" width="1200" height="900" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fca981.167159bf.jpeg&amp;w=1200&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fca981.167159bf.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fca981.167159bf.jpeg&amp;w=3840&amp;q=75">
<p>国航的飞机头等舱、公务舱和后面的经济舱都满了，中间的超级经济舱没有人，这说明了一个啥问题？ 空姐说之所以上大飞机飞法兰克福到北京的航线，主要是因为头等舱和公务舱供不应求...</p>
<p>到达法兰克福机场已经是当地下午 4 点多的时间，没有选择马上转机而是在机场酒店住一晚，这样既不用匆忙过海关，其实主要是第一次到布鲁塞尔，对当地交通不清楚，不知道晚上到了是不是能顺利的找到酒店入住。在机场住的酒店就在航站楼的对面，但是 Google Map 和 Apple Map 都建议我步行 30 分钟，从机场外绕一圈进去。 法兰克福机场的指示牌比较清晰，很快就找到去转火车（BD 那个是转火车还是地铁没搞清楚） 的路就可以直接走过去，实际用时只有 5 分钟多一些。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbear-2.79a843a6.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbear-2.79a843a6.jpg&amp;w=3840&amp;q=75">
<p>就在酒店对面的西区一个超市买了一份沙拉和一瓶啤酒当晚餐了，这种吃法上次在阿姆斯特丹的时候实验是成功的，一点也不会觉得饿，吃完很快就容易睡着，有利于倒时差。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbear-1.577de2f4.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbear-1.577de2f4.jpg&amp;w=3840&amp;q=75">
<p>这个不知道什么牌子的啤酒，这种皮尔森苦苦的感觉还是比较适应，看来以后德国啤酒只能喝这种口味了。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbread-1.4f93bf79.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbread-1.4f93bf79.jpg&amp;w=3840&amp;q=75">
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbread-2.ed5a3535.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbread-2.ed5a3535.jpg&amp;w=3840&amp;q=75">
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbread-3.dbd44e16.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbread-3.dbd44e16.jpg&amp;w=3840&amp;q=75">
<p>买东西的时候路过一家主打做面包的店，看起来非常诱人可口，尤其是看到这种德国特有的圈圈形状的面包，上面撒了海盐粒，配着喝啤酒也是非常不错的选择。还是忍住了没有买，眼大肚子小，等到了慕尼黑再去吃吧。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ffrankfurt.359a15b4.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ffrankfurt.359a15b4.jpg&amp;w=3840&amp;q=75">
<p>清晨起来往窗外拍了一张照片，其实不是法兰克福的市中心，应该是另一个方向的郊区，临走前向右望去才远远的看到标志性建筑。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcrj900.078113f1.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcrj900.078113f1.jpg&amp;w=3840&amp;q=75">
<p>从法兰克福到布鲁塞尔是这种超小的飞机，小到 20 寸的登机箱都不能带上飞机，而且廊桥都接不上，只能坐摆渡车。下了摆渡车，把行李交给工作人员，由他现场装进飞机的行李仓，等落地出飞机后，这些行李箱已经摆在飞机下面，捡起自己的离开就行。法兰克福到布鲁塞尔 300 多公里，飞机 50 分钟就飞到了，感觉在两边机场跑到滑行的时间都比在天上飞的时间长。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-airport-1.318c6e8e.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-airport-1.318c6e8e.jpg&amp;w=3840&amp;q=75">
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-airport-2.9c4466fe.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-airport-2.9c4466fe.jpg&amp;w=3840&amp;q=75">
<p>布鲁塞尔国家机场的人比法兰克福还多，4 月份途径法兰克福机场转机回北京的时候，还有 2/3 的商铺没有开门，这次飞过来的时候听国航的空姐说已经全部都开了。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftrain-1.0c7cd21f.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftrain-1.0c7cd21f.jpg&amp;w=3840&amp;q=75">
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftrain-2.61985b76.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftrain-2.61985b76.jpg&amp;w=3840&amp;q=75">
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmetro.4d5866cc.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fmetro.4d5866cc.jpg&amp;w=3840&amp;q=75">
<p>经过火车倒地铁，顺利入住酒店。从布鲁塞尔机场到阿姆斯特丹每小时有一班火车，2 个小时到达。布鲁塞尔这个 2 号线地铁开门这个感觉是半自动的，需要稍微用点力气拉一下才能自动打开。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-bear-1.9da5ede8.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-bear-1.9da5ede8.jpg&amp;w=3840&amp;q=75">
<p>晚上照例出去买了沙拉和啤酒，随便走走路两边就是这种小酒馆聚集的街区，外面很多人在坐着喝酒聊天。</p>
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-bear-2.3523038b.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-bear-2.3523038b.jpg&amp;w=3840&amp;q=75">
<img alt="" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-bear-3.81bc111f.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-bear-3.81bc111f.jpg&amp;w=3840&amp;q=75">
<p>试了两种啤酒，第二个的口味比较容易接受，有柚子味道的 IPA 更加容易入口，不知道这两款是不是比利时本地生产的啤酒。喝完已经睁不开眼了，啤酒瓶上的字努力半天也没有找到认识的单词，很快就倒下睡去了。</p>
<blockquote>
<p>这种中间转机住一天的方案比较好，睡两晚时差基本上就倒过来了，而且欧这这种晚 6 个小时的时差本来就容易倒过来，不像美国那种 12 个小时的时差，翻来覆去的睡不着，等时差倒过来了反而要回北京了。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[EuroRust 2023 Day 1]]></title>
            <link>https://maquanyi.com/articles/euro-rust-day-1</link>
            <guid>https://maquanyi.com/articles/euro-rust-day-1</guid>
            <pubDate>Thu, 12 Oct 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="" loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OPrkZW8AAGTwu.dbed4d54.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OPrkZW8AAGTwu.dbed4d54.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OPrkZW8AAGTwu.dbed4d54.jpeg&amp;w=3840&amp;q=75">
<p>早晨赶完会场的公交还是挺顺利的，只是听从了导航的意见到了指定的地点下车，其实从前一站下车更容易走到会场里面。从车站下来后找了几次菜找到一个合适的路线进入会场，路上就拍下了远处码头的这张照片。会场其实是在一个 Shopping Mall 的上面，地标建筑非常明显。</p>
<img alt="" loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OR2w-W4AAJCfW.a6ff6fa6.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OR2w-W4AAJCfW.a6ff6fa6.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OR2w-W4AAJCfW.a6ff6fa6.jpeg&amp;w=3840&amp;q=75">
<p>排队签到的人群还是很长，进入会场的速度被保安控制着，虽然比较满但依然是比较有秩序的。排队的人也没有太多寒暄的声音，反而是比较安静的存在。远处平台上三三两两的人端着咖啡低声聊天，天空还不是飘来几点雨滴，整个的氛围和年初在阿姆斯特丹的 KubeCon 完全不同。</p>
<img alt="" loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OWgOXWAAA3Xbd.18f12324.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OWgOXWAAA3Xbd.18f12324.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OWgOXWAAA3Xbd.18f12324.jpeg&amp;w=3840&amp;q=75">
<p>签到后我也没有进入到会场，要了一杯咖啡站在平台四处张望，这个季节的布鲁塞尔已经有点冷了，风吹过来打火机都很难打着火。</p>
<img alt="" loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_JWkAAKzn0.3f78b2d8.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_JWkAAKzn0.3f78b2d8.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_JWkAAKzn0.3f78b2d8.jpeg&amp;w=3840&amp;q=75">
<img alt="" loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_KXkAAl0sH.dfc614d5.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_KXkAAl0sH.dfc614d5.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_KXkAAl0sH.dfc614d5.jpeg&amp;w=3840&amp;q=75">
<p>开场后我走到大厅的最后面，找了一个靠边的位置坐下来，这个位置离讲台比较远，但是可以看到整个大厅的情况，大厅里面的人不多，对比 KubeCon 也就是一个 Session 的听众吧，这比我想象的还要少一些。</p>
<img alt="" loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_JWMAAkz_t.a1541df4.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_JWMAAkz_t.a1541df4.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OXh_JWMAAkz_t.a1541df4.jpeg&amp;w=3840&amp;q=75">
<p>主持人正式开场啦，标准的美国口音让我恍惚觉得这不是在欧洲开会。</p>
<img loading="lazy" width="1024" height="576" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OcOoVa4AAVVHS.fb3a2e08.jpeg&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OcOoVa4AAVVHS.fb3a2e08.jpeg&amp;w=2048&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OcOoVa4AAVVHS.fb3a2e08.jpeg&amp;w=2048&amp;q=75">
<p>开场必须是当红炸子鸡，社区扛把子 Niko ，只是这个版本 Release 的日期让我迷惑，这不是圣诞节假期么。后来想想也对，如果是公司的开源项目，绝不会在这个时候发版本，这不是要让工会给弄死么。社区的项目反而无所谓，大家都是义务的，圣诞节可能空闲的时间更多。</p>
<img alt="" loading="lazy" width="1024" height="576" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OZ-FMboAAFgI3.29b0976c.jpeg&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OZ-FMboAAFgI3.29b0976c.jpeg&amp;w=2048&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OZ-FMboAAFgI3.29b0976c.jpeg&amp;w=2048&amp;q=75">
<img alt="" loading="lazy" width="1024" height="576" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OdkqvaUAAGaEZ.909bd1fb.jpeg&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OdkqvaUAAGaEZ.909bd1fb.jpeg&amp;w=2048&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OdkqvaUAAGaEZ.909bd1fb.jpeg&amp;w=2048&amp;q=75">
<p>演讲中还重点讲了会有 Edition 2024 的发布，我理解这标志着发展到了一个新的阶段。</p>
<img loading="lazy" width="2048" height="1152" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OvNwBaYAEB4Gg.ba77fb60.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OvNwBaYAEB4Gg.ba77fb60.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8OvNwBaYAEB4Gg.ba77fb60.jpeg&amp;w=3840&amp;q=75">
<p>全天对我影响最大的是这个议题，里面提到了这样一本书和一个理念，当然这个理念是已经提出来很长时间了，但是在 Rust 里面并不是流行的开发模式。这个让我瞬间想到当前做的 <a href="https://github.com/open-rust-initiative/dagrs">dagrs</a> 的近况。项目悄无声息的在发布了两个版本在 <a href="https://crates.io/crates/dagrs">crates.io</a> ，没有进行任何的宣传，但是却有了两个用户在尝试使用。这个编排调度的方式肯定是对当前很多开发工作有帮助的，但是我一直找不到一个很好的理论来解释这个逻辑，所以就从没有正式的宣传过。今天瞬间得到了启发，这个项目的目标应该是 <em>Flow-Based Programming in Rust</em> 这个方向啊，就这个我明年报各种 Rust 会议的议题都有了，承包明年一年的 Rust 议题。</p>
<img loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8PfUUeWAAA62vf.304c1aeb.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8PfUUeWAAA62vf.304c1aeb.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8PfUUeWAAA62vf.304c1aeb.jpeg&amp;w=3840&amp;q=75">
<p>这个议题讲了很多实践上的问题，如何设计一个好的 Crate ，内容相当不错，但是 Slide 里面都很简单，没有更加明确的写出来规则，等视频上线了可以拿 AI 分析写一个总结。</p>
<img loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8Pjo04bIAAbFZo.e4d91e68.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8Pjo04bIAAbFZo.e4d91e68.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FF8Pjo04bIAAbFZo.e4d91e68.jpeg&amp;w=3840&amp;q=75">
<p>学习 Rust 从来都是难点，这个总结是非常到位的，当时没有拍下来，从 Twitter 上扒了一张。</p>
<img loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG400.21693eda.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG400.21693eda.jpg&amp;w=3840&amp;q=75">
<p>午饭没有在会场排队是正确的，我在下面商场吃完一个汉堡回来队伍还有很长，致使下午开始的时间都推后，因为还有很多人没吃饭。</p>
<img loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG399.c921ea1e.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG399.c921ea1e.jpg&amp;w=3840&amp;q=75">
<p>吃完午饭在 Shopping Mall 里面溜达，看到这两精致的小车，不知道国内有没有这个小车买，不过国内现在电车如云，这个小车的市场应该不大。</p>
<img loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG398.434be031.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG398.434be031.jpg&amp;w=3840&amp;q=75">
<p>晚上公司还有聚餐活动，由于时差和疲劳，我选择了回酒店休息，这次解锁没有看导航的情况，根据路牌和记忆，竟然走了一个没有走过的线路回到酒店，解锁了新的成就。</p>
<img loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG397.5fdfbd7c.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FWechatIMG397.5fdfbd7c.jpg&amp;w=3840&amp;q=75">
<p>中午的汉堡实在是太硬了，晚上喝了一杯啤酒就已经睁不开眼了。这个味道有点奇怪，开始喝的时候温度低，是酒花加一些果香，后面酒温度上来了开始有烟熏味道，不知道是不是我昨天时差上头产生的幻觉😱。这款酒确实是比利时本地啤酒，明年可以换一个口味尝试一下。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[EuroRust 2023 -  华为办公室参加 Workshop]]></title>
            <link>https://maquanyi.com/articles/euro-rust-workshop</link>
            <guid>https://maquanyi.com/articles/euro-rust-workshop</guid>
            <pubDate>Wed, 11 Oct 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="华为布鲁塞尔办公室窗外的风景" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-huawei.8e4467f4.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fbrussel-huawei.8e4467f4.jpg&amp;w=3840&amp;q=75">
<p>在 EuroRust 开始前，公司在办公室组织了一次 Workshop ，参加的都是公司在 Rust 社区的雇员和顾问。 在毫无准备的情况下和社区的专家用英语交流，主打的就是我不尴尬，尴尬的就是老外。</p>
<img alt="Hilton Garden Inn 的小花园" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhilton-garden-inn-1.cf0ff58e.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhilton-garden-inn-1.cf0ff58e.jpg&amp;w=3840&amp;q=75">
<img alt="Hilton Garden Inn 的小花园" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhilton-garden-inn-2.fbc18d6f.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhilton-garden-inn-2.fbc18d6f.jpg&amp;w=3840&amp;q=75">
<img alt="Hilton Garden Inn 的小花园" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhilton-garden-inn-3.b7ea2f73.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fhilton-garden-inn-3.b7ea2f73.jpg&amp;w=3840&amp;q=75">
<p>在酒店的两栋楼之间有个小小的花园，是可以室外吸烟和喝啤酒的地方。角落里种了不同的花，我一个名字都不知道，不要问我是ß怎么在生物系毕业的...</p>
<img alt="Workshop 晚餐" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdinner-1.bf2d7f96.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdinner-1.bf2d7f96.jpg&amp;w=3840&amp;q=75">
<p>晚上也是和 Workshop 的同事一起聚餐，餐前的甜点是这种炸的芝士球，里面是芝士，外面是面包屑，炸的很酥脆，很好吃。这就简直就是比利时版本的炸糕啊...</p>
<img alt="Workshop 晚餐" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdinner-2.2584bbe1.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdinner-2.2584bbe1.jpg&amp;w=3840&amp;q=75">
<p>比利时大部分是讲法语的，餐馆服务员也是讲法语，在场也只有一个人会讲法语，帮助大家进行翻译，菜单是法语、德语和英语的三语菜单。我开始看到一个写了 Beef 的菜，就准备试试，心想牛肉怎么做也不会太出圈，应该是可以的。 幸亏对面的意大利同事跟我说这个是全生的牛肉，吓得我赶紧换了一道牛排。但是他点了这个全生的牛肉，上来的时候感觉是一对牛肉馅。不得不说，我点的这个牛排还是相当好吃，感觉这顿吃完几天都不想吃肉了。</p>
<img alt="布鲁塞尔夜景" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnight-1.2da07ee0.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnight-1.2da07ee0.jpg&amp;w=3840&amp;q=75">
<img alt="布鲁塞尔夜景" loading="lazy" width="4032" height="3024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnight-2.d1e2a6db.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fnight-2.d1e2a6db.jpg&amp;w=3840&amp;q=75">
<p>晚餐结束，由于我没有装 Uber 也不知道怎么打车，所以决定按原路坐公交车回酒店。 一路上倒是没有遇到什么问题，只是晚上 9 点后的地铁上，都是三三两两拿着啤酒的醉汉，甚是可怕。吓得我第二天赶紧装上了 Uber。如果再遇到晚上回酒店，还是打车更加方便一些。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[为 GPG 生成的证书续签有效期]]></title>
            <link>https://maquanyi.com/articles/expire-gpg</link>
            <guid>https://maquanyi.com/articles/expire-gpg</guid>
            <pubDate>Sun, 28 Jan 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>今天在提交代码的时候使用 <code>git commit -a -s -S -m</code> 这个命令签名报错, 错误内容包括：</p>
<pre class="language-bash"><code class="language-bash">错误：gpg 无法为数据签名
</code></pre>
<p>开始以为是最近升级了 <code>gnupg</code> 到最新的 <code>2.4.4</code> 引发的错误，后来仔细看了一眼发现是去年生成的证书过期了。 可以使用 <code>gpg --with-keygrip -k XXXX</code> 可以检查当前证书的状态，我的显示</p>
<pre class="language-bash"><code class="language-bash">pub   rsa4096 <span class="token number">2022</span>-01-23 <span class="token punctuation">[</span>SC<span class="token punctuation">]</span> <span class="token punctuation">[</span>过期于：2024-01-23<span class="token punctuation">]</span>
</code></pre>
<p>搜了一下其实延期并不麻烦，使用 <code>gpg --edit-key XXXX</code> 进入编辑模式，然后输入 <code>expire</code> ，然后输入 <code>save</code> 保存即可。</p>
<pre class="language-bash"><code class="language-bash">gpg<span class="token operator">&gt;</span> expire
将要变更主密钥的过期时间。
请设定这个密钥的有效期限。
         <span class="token number">0</span> <span class="token operator">=</span> 密钥永不过期
      <span class="token operator">&lt;</span>n<span class="token operator">&gt;</span>  <span class="token operator">=</span> 密钥在 n 天后过期
      <span class="token operator">&lt;</span>n<span class="token operator">&gt;</span>w <span class="token operator">=</span> 密钥在 n 周后过期
      <span class="token operator">&lt;</span>n<span class="token operator">&gt;</span>m <span class="token operator">=</span> 密钥在 n 月后过期
      <span class="token operator">&lt;</span>n<span class="token operator">&gt;</span>y <span class="token operator">=</span> 密钥在 n 年后过期
密钥的有效期限是？<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
</code></pre>
<p>我这里选项的 <code>1y</code>，明年在继续续期，如果忘记了就翻出来这篇文章参照。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Meta 开源 Sapling 客户端，推动 monorepo 模式发展]]></title>
            <link>https://maquanyi.com/articles/facebook-sapling-mercurial-hg-git-monorepo</link>
            <guid>https://maquanyi.com/articles/facebook-sapling-mercurial-hg-git-monorepo</guid>
            <pubDate>Thu, 01 Dec 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img loading="lazy" width="2545" height="835" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsapling.20a89dc1.webp&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsapling.20a89dc1.webp&amp;w=3840&amp;q=75">
<h3>1. Sapling 是什么？</h3>
<p><strong><del>Facebook</del></strong> <strong>Meta</strong> 悄悄 <a href="https://engineering.fb.com/2022/11/15/open-source/sapling-source-control-scalable">发布</a> 新的版本管理工具 - <a href="https://sapling-scm.org">Sapling</a> 。网站首页打出 <strong>"A Scalable, User-Friendly Source Control System"</strong> 的 <strong>Slogan</strong> ， 直击当前最普遍使用的版本管理工具 <strong>Git</strong> 的痛点。</p>
<p><strong>Git</strong> 作为全世界开发者都在使用的版本管理工具，也是有一定学习曲线。 当然如果知道 <code>clone</code>、<code>pull</code>、<code>commit</code> 和 <code>push</code> 四个命令就可以完成日常 90% 以上的工作，所以少有人知道 <strong>Git</strong> 版本管理核心是 <strong>Snapshot</strong> ，很多人错误的以为 <strong>Git</strong> 的逻辑是 <strong>diff</strong> 。 <strong><a href="https://github.com">GitHub</a></strong> 这些年做出了很多努力帮助开发者使用 <strong>Git</strong>，像 <strong>LFS 协议</strong> 和它的实现就是一个突出的贡献。 <strong><a href="https://github.com">GitHub</a></strong> 是通过自身的服务，降低 <strong>Git</strong> 的使用门槛，并形成了一个完备的生态系统 <strong><del>"绑架"</del></strong> <strong>"俘虏"</strong> 了开发者的心。 <a href="https://sapling-scm.org">Sapling</a> 是不是真的对开发者友好，我没有深入使用并没有发言权，目前我的研究方向是在版本管理工具的 <strong>"Scalable"</strong> 能力上。</p>
<p>谈起版本管理工具的 <strong>Scalable</strong> ，就要先讲 <strong>monorepo</strong> 的概念了。众所周知，<strong>"不存在"</strong> 的互联网三大巨头 <a href="https://google.com">Google</a>、<a href="https://facebook.com">Facebook</a> 和 <a href="https://twitter.com">Twitter</a> 三家是采用 <strong>monorepo</strong> 模式，只是各家技术栈不同。</p>
<p><a href="https://google.com">Google</a> 的版本管理系统叫做 <strong>Piper</strong> ，在 <strong>Piper</strong> 的相关论文中提到截止到 2015 年已经存储了 9 百万个文件，使用了 <strong>86 TB</strong> 存储空间，估计现在存储的文件应该达到 <em>billion</em> 级别了。 <strong>Piper</strong> 是 <a href="https://google.com">Google</a> 通过修改 <a href="https://www.perforce.com">Perforce</a> 并把它构建在其强大的基础设施之上，实现了海量的单一代码仓存储能力，同时 <a href="https://google.com">Google</a> 还有一个自己的客户端 <strong>CitC</strong> ，能够兼容 <strong>Git</strong> 在本地使用。</p>
<p><a href="https://facebook.com">Facebook</a> 的版本管理系统 <strong>Sapling</strong> 是在 <a href="https://www.mercurial-scm.org">Mercurial</a> 的基础做了修改，使其能够支持 <strong>monorepo</strong> 模式的开发，此次开源的就是这个系统的客户端。 而 <a href="https://twitter.com">Twitter</a> 公司是在 <a href="https://git-scm.com">Git</a> 的基础上做增强使其能够支持 <strong>monorepo</strong> 模式。对于 <a href="https://facebook.com">Facebook</a> 和 <a href="https://twitter.com">Twitter</a> 目前系统存储的容量少有公开的报道。</p>
<p>从 <a href="https://sapling-scm.org">Sapling</a> 的文档看，其存储逻辑、命令和使用方式已经是独立发展，我觉得可以和 <a href="https://www.mercurial-scm.org">Mercurial</a> 一样，算作是一个独立的版本管理工具。期望 <strong>Meta</strong> 能尽快把配套的 <em>服务端</em> 开源，让我们一窥 <strong>Sapling</strong> 的真容。</p>
<h3>2. Git、Mercurial 和 Sapling 底层存储逻辑的差异</h3>
<p>当研究 <strong>Monorepo</strong> 的 <strong>Scalable</strong> 的问题时，我发现多数版本管理系统产生和发展都是在开源社区中。开源社区的代码仓库体量相对比较小，和任意一个中型公司的代码仓库相比都是几个数量级的差距。但是开源社区的另一个显著的特点就是分散性，贡献代码的开发者居住在世界各地，所以开源社区的版本管理系统的第一要解决的问题就是分布式。既要开发者本地有完整的版本可以进行开发，又要能够将本地的版本通过邮件、HTTP 等协议推送到远程的版本仓库中，这就是 <strong>Git</strong> 和 <strong>Mercurial</strong> 的 <strong>"产品"</strong> 思路。</p>
<blockquote>
<p>这几年我从事开发和运营开源项目都是按照 <strong>"产品"</strong> 思路去考虑，发现这样容易看懂开源项目的本质。</p>
</blockquote>
<p>要保证开发人员本地拥有完整代码，所以多数版本管理工具会采用文件系统，通过各种编码等方式来存储源代码文件。<strong>Git</strong> 把所有文件保存在 <code>.git</code> 目录，<strong>Mercurial</strong> 把保存在 <code>.hg</code> 目录。这两个目录都是隐藏的，所以开发者在开发的时候是看不到的。这两个目录中存储的是 <strong>Git</strong> 和 <strong>Mercurial</strong> 的版本管理元数据，包括文件的版本信息、文件的变更信息、文件的历史版本等等。当开发的时候，<strong>Git</strong> 和 <strong>Mercurial</strong> 会 <code>checkout</code> 一个指定版本的代码到 <strong>工作目录</strong> ，所有的工作都这这个目录下进行，当开发完成后，再将这个目录下的代码提交到 <strong>Git</strong> 或者 <strong>Mercurial</strong> 的版本仓库中。</p>
<h4><strong>2.1 Git Internals - Content-addressable FileSystem</strong></h4>
<blockquote>
<p>这里引用 <a href="https://www.youtube.com/playlist?list=PL9lx0DXCC4BNUby5H58y6s2TQVLadV8v7">Git Internals</a> 这个视频的内容和图片，这个系列的视频是我看到讲解最清楚的教程。</p>
</blockquote>
<p><strong>Git</strong> 把开发者提交文件的二进制完整保存在一个 <strong>blob</strong> 对象中。 不论是添加新文件还是修改文件的部分内容，都会 <strong>全量</strong> 保存文件的二进制数据。 <strong>Git</strong> 对每个 <strong>blob</strong> 对象都计算 <strong>SHA1</strong> 值做为这个对象的 <strong>ID</strong> 。</p>
<p><strong>Git</strong> 把一个或者多个 <strong>blob</strong> 对象的 <strong>ID</strong> 保存在一个 <strong>tree</strong> 对象中，同时还在 <strong>tree</strong> 对象中记录了这个文件的名字， <strong>tree</strong> 对象就是每次开发者新加、修改的文件 <strong>清单</strong> 。 <strong>Git</strong> 对 <strong>tree</strong> 对象计算 <strong>SHA1</strong> 值做为对象的 <strong>ID</strong> 。</p>
<p><strong>Git</strong> 最后把 <strong>tree</strong> 对象的 <strong>ID</strong> 保存在 <strong>commit</strong> 对象中， <strong>commit</strong> 对象同时包含了提交的作者名字、邮件地址、提交时间、描述和 <strong>parent tree</strong> 等信息。 <strong>Git</strong> 对每个 <strong>commit</strong> 对象计算 <strong>SHA1</strong> 值做为对象的 <strong>ID</strong> 。</p>
<p>这样形成了一个 <strong>Git</strong> 版本管理的数据链路，如下图所示：</p>
<img loading="lazy" width="900" height="344" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgit-internals.d5122154.png&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgit-internals.d5122154.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgit-internals.d5122154.png&amp;w=1920&amp;q=75">
<p>重点的是，每次文件修改都会保存一个新的 <strong>blob</strong> 对象，通过这样的 <strong>link</strong> 方式，可以形成一个完整的仓库的历史版本。</p>
<h4><strong>2.2 Mercurial Internals</strong></h4>
<p><strong>Mercurial</strong> 在存储上和 <strong>Git</strong> 完全不同， <strong>Mercurial</strong> 使用 <strong>revlog</strong> 的格式来存储对象，每个文件的不同版本都保存中一个文件中。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">hg</span> debugindex .hg/store/data/src/pb.c.i
<span class="token function">rev</span>   offset  length   base linkrev nodeid       p1           p2
<span class="token number">0</span>        <span class="token number">0</span>     <span class="token number">467</span>      <span class="token number">0</span>    <span class="token number">10</span>  a7bdd2379025 000000000000 000000000000
<span class="token number">1</span>      <span class="token number">467</span>     <span class="token number">168</span>      <span class="token number">0</span>    <span class="token number">12</span>  692932a95c0d 000000000000 a7bdd2379025
<span class="token number">2</span>      <span class="token number">635</span>     <span class="token number">173</span>      <span class="token number">0</span>    <span class="token number">15</span>  f1d9cb4201e4 692932a95c0d 000000000000
<span class="token number">3</span>      <span class="token number">808</span>     <span class="token number">476</span>      <span class="token number">0</span>    <span class="token number">17</span>  d238a6113e4c f1d9cb4201e4 000000000000
<span class="token number">4</span>     <span class="token number">1284</span>     <span class="token number">491</span>      <span class="token number">0</span>    <span class="token number">18</span>  b71d299270a5 f1d9cb4201e4 000000000000
<span class="token number">5</span>     <span class="token number">1775</span>     <span class="token number">470</span>      <span class="token number">0</span>    <span class="token number">19</span>  4a7ebb32f962 b71d299270a5 d238a6113e4c
<span class="token number">6</span>     <span class="token number">2245</span>      <span class="token number">64</span>      <span class="token number">0</span>    <span class="token number">20</span>  6b99ca4dde14 4a7ebb32f962 000000000000
<span class="token number">7</span>     <span class="token number">2309</span>     <span class="token number">177</span>      <span class="token number">0</span>    <span class="token number">21</span>  33557d969679 d238a6113e4c 000000000000
<span class="token number">8</span>     <span class="token number">2486</span>     <span class="token number">213</span>      <span class="token number">0</span>    <span class="token number">22</span>  e4d67566afd0 6b99ca4dde14 33557d969679
<span class="token number">9</span>     <span class="token number">2699</span>     <span class="token number">102</span>      <span class="token number">0</span>    <span class="token number">23</span>  ab4bcfb966f8 33557d969679 000000000000
<span class="token number">10</span>    <span class="token number">2801</span>     <span class="token number">384</span>      <span class="token number">0</span>    <span class="token number">24</span>  86d19e47e6d0 e4d67566afd0 000000000000
<span class="token number">11</span>    <span class="token number">3185</span>      <span class="token number">88</span>      <span class="token number">0</span>    <span class="token number">25</span>  4969c00e0bc8 86d19e47e6d0 ab4bcfb966f8
</code></pre>
<p><strong>revlog</strong> 格式定义两个文件，<code>.d</code> 文件包含实际的文件数据，<code>.i</code> 文件是索引文件，记录不同版本在 <code>.d</code> 文件中 offset ，使得检索起来速度更快。 在 <code>.d</code> 文件中即可以存储文件的完整内容（这跟 <strong>Git</strong> 的 <strong>blob</strong> 对象的逻辑是一样的），也可以存储和上一个版本的二进制差异（这与 <strong>Git</strong> 的 <strong>Pack</strong> 文件存储逻辑相通，关于 <strong>Pack</strong> 文件的格式请自行 <em>Google</em> 之）。为了提升检索速度，每隔一定版本 <strong>Mercurial</strong> 会保存一个全量文件的快照，这样版本回溯的时候就大大节省了时间。</p>
<p>对应 <strong>Git</strong> 的 <strong>tree</strong> 对象，<strong>Mercurial</strong> 也有类似的概念，叫做 <strong>manifest</strong> ， <strong>manifest</strong> 保存了文件的名字和 <strong>revlog</strong> 的索引文件的对应关系，这样就可以通过文件名字来找到对应的 <strong>revlog</strong> 文件。</p>
<p>对应 <strong>Git</strong> 的 <strong>Commit</strong> 对象，<strong>Mercurial</strong> 使用的是 <strong>changeset</strong> ，也是包含了一次提交的各种信息。</p>
<p>虽然存储方式和 <strong>Git</strong> 完全不同，但是 <strong>Mercurial</strong> 也是可以形成一个完整的版本管理的数据链路。</p>
<pre class="language-bash"><code class="language-bash">   .--------linkrev-------------.
   <span class="token function">v</span>                            <span class="token operator">|</span>
.---------.    .--------.    .--------.
<span class="token operator">|</span>changeset<span class="token operator">|</span> .-<span class="token operator">&gt;|</span>manifest<span class="token operator">|</span> .-<span class="token operator">&gt;|</span><span class="token function">file</span>    <span class="token operator">|</span>---.
<span class="token operator">|</span>index    <span class="token operator">|</span> <span class="token operator">|</span>  <span class="token operator">|</span>index   <span class="token operator">|</span> <span class="token operator">|</span>  <span class="token operator">|</span>index   <span class="token operator">|</span>   <span class="token operator">|</span>--.
<span class="token variable"><span class="token variable">`</span>---------' <span class="token operator">|</span>  <span class="token variable">`</span></span>--------<span class="token string">' |  `--------'</span>   <span class="token operator">|</span>  <span class="token operator">|</span>
    <span class="token operator">|</span>       <span class="token operator">|</span>      <span class="token operator">|</span>      <span class="token operator">|</span>     <span class="token operator">|</span> <span class="token variable"><span class="token variable">`</span>-------'  <span class="token operator">|</span>
    V       <span class="token operator">|</span>      V      <span class="token operator">|</span>     V    <span class="token variable">`</span></span>-------<span class="token string">'
.---------. |  .--------. |  .---------.
|changeset|-'</span>  <span class="token operator">|</span>manifest<span class="token operator">|</span>-<span class="token string">'  |file     |
|data     |    |data    |    |revision |
`---------'</span>    <span class="token variable"><span class="token variable">`</span>--------'    <span class="token variable">`</span></span>---------'
</code></pre>
<blockquote>
<p>我对 <strong>Mercurial</strong> 的研究不多，如果以上描述有错误请 <strong>TG</strong> 联系我，ID <code>genedna</code>。</p>
</blockquote>
<h4><strong>2.3 Sapling Internals</strong></h4>
<p><strong>Sapling</strong> 采用了一个叫 <strong>IndexedLog</strong> 的格式来存储对象，这个格式是 <strong>Sapling</strong> 自己设计的，目的是为了提升 <strong>Sapling</strong> 的性能，解决 <strong>Mercurial</strong> 和 <strong>Git</strong> 的各种问题。 它的目标：</p>
<ol>
<li>O(log N) 查找，不需要重新打包以保持性能。这个主要是针对 <strong>Git</strong> 面对大仓库时需要把对象打包成 <strong>Pack</strong> 文件的问题，这样会导致查找性能下降。</li>
<li>通过哈希值插入，没有拓扑顺序限制。 这个主要是针对 <strong>Mercurial</strong> 的 <code>.d</code> 文件是 <strong>append</strong> 的方式修改，无法在中间插入新的对象。</li>
</ol>
<p><strong>Sapling</strong> 的 <strong>IndexedLog</strong> 对应的是 <strong>Mercurial</strong> 的 <strong>revlog</strong> ，结构上采用了一个 <strong>log</strong> 文件和多个 <strong>index</strong> 文件的方式。<strong>log</strong> 文件对应 <strong>Mercurial</strong> 的 <code>.d</code> 文件，<strong>index</strong> 文件对应 <strong>Mercurial</strong> 的 <code>.i</code> 文件，但是 <strong>Sapling</strong> 有多种 <strong>index</strong> 。</p>
<ol>
<li><strong>Index</strong> 是对应 <strong>Rust</strong> 的函数，是没有办法序列化使用，相当于应用在内存中的索引。</li>
<li><strong>Standalone Index</strong> 是相当于应用在磁盘上的索引，类似于 <strong>Rust</strong> 的 <code>BTreeMap&lt;Vec&lt;u8&gt;, LinkedList&lt;u64&gt;&gt;</code>结构，可以序列化和反序列化。</li>
</ol>
<hr>
<h3>总结</h3>
<p>从 <strong>Sapling</strong> 的介绍文章和文档来看，目前放出的只是一个客户端，如果面对大仓库的话，还差虚拟文件系统和服务端。虚拟文件系统的思路有点像微软解决 <strong>Git</strong> 超大仓库采用的方式；<strong>延迟下载</strong> 的逻辑其实 <strong>Git</strong> 也可以采用 <strong>Shallow Clone</strong> <code>--depth=1 --single-branch --branch=&lt;branch&gt;</code> 的方式获取 <code>HEAD</code> 指向的 <strong>Snapshot</strong>，而不是全部下载。</p>
<p>对于提交，不管是那种版本管理工具都对提交的内容进行了哈希，有的存储全量快照，有的存储二进制的差异。如果以全量快照的方式来看，哈希值是 <strong>Key</strong> ，提交的文件或者内容是 <strong>Value</strong> ，形成了一组唯一的 <strong>Key-Value</strong>。如果采用大型的分布式数据库对这些 <strong>Key-Value</strong> 对进行存储，那么就可以实现一个分布式的大型版本管理系统。其实 <a href="https://google.com">Google</a> 的版本管理系统 <strong>Piper</strong> 正是采用这种逻辑将数据存储到它的全球一致性数据库 <strong>Spanner</strong> 中。</p>
<p>仅仅进行存储还不能解决我们生产中的问题，其中最主要的有两个：</p>
<ol>
<li>对于 <strong>monorepo</strong> 需要采用 <a href="https://trunkbaseddevelopment.com">Trunk Based Development</a> 的研发模型，这对很多公司都是不可想象的，尤其是是很多开发者已经习惯 <strong>GitHub PR</strong> 或者基于 <strong>Git Flow</strong> 的开发模型，同时也对研发管理、代码 Review、生产部署等带来巨大的冲击。</li>
<li>对于 <strong>monorepo</strong> 必须有一个超大型的 <strong>构建系统</strong> 进行支撑。<a href="https://google.com">Google</a> 开源了 <a href="https://bazel.build">Bazel</a>，<a href="https://facebook.com">Facebook</a> 开源了 <a href="https://github.com/facebook/buck">Buck</a>，<a href="https://twitter.com">Twitter</a> 开源了 <a href="https://www.pantsbuild.org">Pants</a> 。 没有这样大型的构建系统能力， <strong>monorepo</strong> 也是水中望月而已。</li>
</ol>
<hr>
<blockquote>
<p><strong>Sapling</strong> 的底层存储使用 <strong>Rust</strong> 开发，<strong>Mercurial</strong> 的底层存储也启动了 <strong>Rust</strong> 的改造计划，<a href="https://github.com/Byron/gitoxide">gitoxide</a> 是目前最完善的 <strong>Rust</strong> 的 <strong>Git</strong> 客户端实现。 加上还不被大家所知晓的使用 <strong>Rust</strong> 开发，号称下一代版本管理工具的 <a href="https://pijul.org">Pijul</a>，可以看到版本管理系统在用 <strong>Rust</strong> 重构底层存储的趋势，<strong>Rust</strong> 的明星项目有望会在这个领域脱颖而出。</p>
</blockquote>
<div style="height:400px;width:auto" data-ntpc="YouTubeEmbed"><lite-youtube videoid="fWMKue-WBok" height="400" params="controls=0"></lite-youtube></div>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[从开源社区的运营到社区基础设施产品的设计]]></title>
            <link>https://maquanyi.com/articles/from-open-source-community-to-production-design</link>
            <guid>https://maquanyi.com/articles/from-open-source-community-to-production-design</guid>
            <pubDate>Fri, 04 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>2020 年 4 月份回到华为参与 openEuler 操作系统社区的开源运营，我来说是有极大的挑战。“开源社区运营” 是一个新的名词（职位），所做的工作内容和界限是没有界定的，一切对于我来说大都是陌生的。当然经过两年的工作，对开源社区运营有了自己的认知，全然不是对这个词的定义，只是流水简述我这两年的思考。</p>
<p>大多数人理解的开源社区运营，就是组织一些开发者活动，线下的 Meetup 也好或是线上的直播，甚至更大规模的开发者会议等等。我妄自猜这可能来自于现在很多互联网公司的开源项目运营工作，大多是由公司市场部门的人担当，所以传统的市场工具加上开源社区传统手段，就组成了普遍意义的开源社区运营工作，也许再设计几个新晋 “网红” ，就算没有可圈可点的结果，也能说的上中规中矩。 当然我觉得这也是开源运营的一部分，但并不能是全部。在我个人的理解，以上都是手段而已，是完成公司内部各种 KPI 指标方法。最近由于有了更多的空闲时间，所以经常思考开源社区运营的核心目的是什么，是各种 star、fork 或者是 contirbutor 数量，还是帮助项目获取更多的商业客户？翻看之前仅存的一些文字资料，这一次演讲（ <a href="/articles/open-source-community-and-operating">开源社区与运营 - 2021 上海计算机教育年会演讲稿</a> ）中随口说的话也许正是我的初心。</p>
<blockquote>
<p><strong>“我的工作就是让这些人(开发者)能够在社区产生更深层次的活动，产生新的想法并且鼓励大家把它变成现实”</strong></p>
</blockquote>
<p>当我们把 <strong>“开源”</strong> 和 <strong>“社区”</strong> 两个词叠加在一起，就得到了一个组合。华为内部有个黑话可以解释这种组合，这个词就是 <strong>“正交”</strong> 。对于 openEuler 社区来讲，这个共同感兴趣的实体就是 openEuler 开源操作系统，这个社区就是属于下载安装使用的那些开发者、在 gitee 上提交代码的开发者、参加社区会议的开发者、还有那些看到 openEuler 各种宣传但因为拖延症晚期还没有去尝试更深入了解的人。我的工作就是让这些人能够在社区产生更深层次的活动，产生新的想法和 Idea 并且鼓励大家把它变成现实。</p>
<hr>
<p>在做开源社区的运营时，首要的就是分析你要运营的开源社区，它的核心技术是什么。我在运营 openEuler 操作系统社区的同时，还负责管理 openGauss 数据库社区和 openLooKeng 大数据引擎社区两个项目的运营工作。 从技术角度上来说，openGauss 数据库和 openLooKeng 大数据引擎是技术领域非常直接的开源社区，使用它们的开发者和能参与到开发的开发者画像是非常清晰的。这部分人群相对容易锁定，运营工作相对就容易 <strong>“聚焦”</strong>（专注）。但是操作系统是非常传统的技术领域，社区演进之慢可以用十年作为单位，而且操作系统涉及到技术领域非常多，很难说一个问题是属于操作系统的问题还是某个特定技术领域。我们传统的认为操作系统的开发者是 Linux Kernel 开发者，其实对于他们来讲，操作系统的发行版来讲并不是他们关心的事情，在 Linux Kernel 上游开发对于任何一个发行版都是收益的。从事操作系统维护的开发者工作说出来是有些无聊，是把各种上游开源软件打包成操作系统指定的包格式（ 对于 openEuler 操作系统来讲是 rpm 格式的软件包 ）。 一个操作系统社区的维护能力，并不是有多少人参与到软件包的维护过程，而是能和上游有多少链接能力，能够快速获取上游的 CVE 漏洞信息，参与到软件的开发中。从这个角度去看，一个真正的操作系统社区其实范围并不大，而且活跃度不高。在国内做这方面工作的开发者并不多，主要是之前国内的操作系统厂商、互联网厂商使用操作系统都会选择 CentOS 或 Debian ，从上游获取软件包就可以，何必要自己去编写软件包的 Spec 。</p>
<p>所以把 openEuler 建设成为一个活跃开源社区的路径就剩下一个比较现实的路径，就是扩大社区的范围，引入一些其它的开源项目也就是 <strong>“创新项目”</strong> 进入社区。其实在 Fedora 社区也是有类似的方式，譬如当时 Fedora 开始做 Atomic ，虽然后来收购了 CoreOS 就融合了（放弃了）。openEuler 里面有一系列的项目，其实里面不乏非常有意思和有价值的项目，譬如我个人最看好的 <a href="https://gitee.com/openeuler/stratovirt">StratoVirt</a> ，这是一个基于 rust-vmm 的项目，但是并没有激起人们参与的热情和动力。 也许是这个技术领域的门槛太高，或许是 Rust 的开发太难，希望有一天我能静下来的时候可以去仔细分析它的发展路径。</p>
<p>回到事情的本质，我们都希望通过开源项目帮助我们的商业融入一个生态或者创造一个生态，我们经常理所当然的认为我们的开源项目会得到关注，理所当然的认为别人会对项目欣然接受和追捧，现实往往是相反的而且是残酷的，多数情况我们的项目并不被人接受，甚至各种负面和诋毁的舆情充斥互联网。为了战胜负面的信息，经常会大举进行宣传，宣传的同时就一定有虚假的部分，然后就进入死循环中。也同时造成了运营和 Marketing 的工作界限越来越模糊，要不沦为工具人要不变成边缘人物。</p>
<p>在内困外乏的处境下，需要先解决最基本的问题，对于初期的核心开发者，是否能够相互顺畅交流决定了这个社区是否能够顺利起步的关键。 openEuler 最早的困境是大部分开发人员是华为的雇员，另外一部分贡献者是国内主流几家操作系统厂商的雇员。由于大部分人不习惯使用邮件列表这个传统的通信方式，造成了相互之间的信息隔离，很多在华为内部的信息在无意的情况下不能传递到整个社区，社区的讨论和决策华为内部无法获取。2020 年因为疫情突发，大家相对熟悉在线会议的形式进行沟通，打破华为内部的闭环通信系统和非华为开发者之间会议通信壁垒的时机成熟已经趋于成熟。为了统一社区的通信交流方案，同时要适应国内开发者的使用习惯，最终设计了一个 openEuler 小程序，通过它调用 Zoom（华为工程师可以接入的唯一外部会议平台）的 API 创建会议来实现社区会议统一创建，实现了华为开发者和外部开发者会议预定及信息获取统一，避免了使用不同的会议系统造成无法信息互通。在一年半的时间里面，举行了 672 次会议，共 676 个小时、3124 位开发者参与过这些会议。社区中大多数的 SIG 组都已经习惯按时组织会议沟通进展，讨论技术路线。当然现在邮件列表的使用还是没有得到普及，国内的更习惯在一些封闭的环境下进行交流。</p>
<p>这个小程序并没有后续再做更多的功能，只是增加了 Meetup 的组织、报名和签到的基本功能。主要是由于我对产品设计的理念，不喜欢做 All In One 这种平台，希望能够随着实际需求发展产品以帮助社区。这种节制我认为是一个产品经理的最基本要求和能力，当然也是最难把握的事情。甚至在小程序的基本功能中，我都做了一些取舍，譬如采用手工的方式维护订阅会议的权限（目前只有 Maintainer 才能预定相关 SIG 的会议）。可以通过在微信中搜索 “openEuler” 来体验这个半成品，虽然未尽的想法无法实现，但是坚定了在后续的工作中以 “开源基础设施产品” 的方式来运营开源社区的思路。</p>
<hr>
<img loading="lazy" width="1202" height="1230" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftwitter.5c99a42c.jpeg&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftwitter.5c99a42c.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftwitter.5c99a42c.jpeg&amp;w=3840&amp;q=75">
<p>在即将离开 openEuler 开源社区运营工作的时候，在 <a href="https://twitter.com/genedna/status/1537386515382550530">Twitter</a> 上看到有 <a href="https://summer-ospp.ac.cn">暑期系列</a> 活动的一个 Tweet 。暑期系列活动其实没有什么新意，完全是在国内照搬 Google Summer of Code （GSoC）的活动，可能也就是比国内其它的高校竞赛做的更用心一些。活动最初是由中国科学院软件研究所提出的，希望在国内高校培养在校生参与开源的能力和热情，第一年（2020）年的时候从 4 月初开始一起策划 <strong>暑期 2020</strong> ，到 7 月份学生进入开发阶段有些匆忙，但是和软件所的团队一起完成了任务收集、学生招募同时还组织了直播活动。而后又经过扩大规模的 <strong>暑期 2021</strong> ，第一年在流程中遇到的问题在流程中得到了解决，更多的学生积极参与进来，尤其是这一年有一些海外的学生报名了活动。经过 2021 年的活动，国内的各种组织机构或几家大型公司都看到这个活动并争相模仿，2022 年类似的活动就有 6、7 个。当然对于广大的高校学生来说，即使这样的规模再扩大 100 倍也无法覆盖相关专业的所有学生。看到能激励学生通过参与开源社区更好的学习和实践，能勇于探索课程外的技术和社区，我想暑期系列项目的目标就达到了。也许暑期系列的竞赛活动，是我这两年运营工作留在社区最好的财富，一个 “正能量” 的事情，一个曾经激励过别人的事情，一个被大家认可的事情，没想到我还能 <strong>“阴差阳错”</strong> 的做出一个让我内心觉得感动的事情。</p>
<p>暑期系列活动是在特定时间内的活动，暑期中的任务也是有限的，当然我也想去影响那些没有被比赛覆盖到同学，希望他们一样能够通过其它的活动获得机会。所以策划了 “开源社区高校实习” ，参与的学生可以通过参与社区开发、测试或者其它社区活动获得积分，通过积分换取实习证明，有一些学校已经承认这些实习证明。这样的一个开放的活动，能够容纳更多有兴趣的同学。但是这个活动还没有像 <strong>暑期系列</strong> 活动被同学所知道。目前社区里面参与这个活动的开发者还不多，没有足够的导师在社区中帮助参与的同学。这是一个正循环的轮子，启动的时候需要很多动力。我不再从事社区的运营工作，希望未来可以投入更多的时间到辅导学生的工作中，这也是我在社区里面能继续做下去的事情。当然这个活动也很快被复制到部门运营的其他几个开源项目里面，AI 相关的项目学生参与度远远高于操作系统，这也是基础软件这几年衰败的原因之一吧。</p>
<p>如何能让社区覆盖其余没有参与社区的学生呢，再苦苦思考之后最终选择采用 <strong>“MOOC”</strong> 的形式来最大限度的把开源社区推广到学生群体中，所以有了 <a href="https://moocstudio.openeuler.sh">MoocStuido</a> 的这个产品（当然它也被快速的复制到部门运营的其他开源项目中）。 其实在名字上这个事情是非常纠结的，最后加上 Studio 是希望把这个平台的开放性变成现实。制作一个课程是非常简单的，只要按照标准格式（其实是 markdown 格式的衍生）就可以制作课程，提交到社区审核后就会上线到平台。 整个产品没有设计的创新，基本都是参考 KataCode 平台。当然就在这个过程中，听到了 KataCode 即将关闭的消息。对于社区要维护一个平台为开发者服务，背后的成本不止是服务器的费用，还有很多工程师的辛苦工作。KataCode 的关闭，对于如何将来运营 MoocStudio 让我产生了很大的焦虑，一个为开源社区服务的产品（平台）不为盈利，这样的平台能存活下来多久。</p>
<p>当我做了很多尝试如何去激活这个开源社区，回顾起来我好像在产品和社区之间迷失了。这是一个操作系统的开源社区，还是需要围绕操作系统来思考。一个操作系统社区的核心是维护软件包，无论塞进去什么创新项目，本质并不能发生变化。维护软件包的人相对比较固定，怎么让更多的人了解到这个最传统的领域从而参与进来呢。Debian 是最社区化的操作系统，有 200 多个衍生版本基于 Debian 制作的，Ubuntu 早起也是基于 Debian 系统发展起来。无疑这是一个扩大社区，扩大影响力的最佳方式，让更多的人参与 openEuler 操作系统的衍生版本制作。所以有了 <a href="https://github.com/omnibuildplatform">OmniBuildPlatform</a> 这个开源项目，把构建一个衍生版的工作做成一个服务，让所有人都有机会做自己的衍生版，它还在持续的改进中，有临时的 <a href="https://omni.test.osinfra.cn">测试平台</a> 可以访问最新的版本，但是并不稳定并不能保证结果可以运行。这个系统做稳定还需要一段时间，不知道我离开当前工作后这个项目是否还会持续，遗憾的是没有用 Rust 开发这个平台。 当然这是个开源的项目，我会竭尽让它继续运转下去。</p>
<p>很多事情会打破已有的规则和生态，但有时候需要打破已有的平衡才能建立新的世界，在新秩序建立的过程中，会有合适的产品（项目）成为大家的选择。在这个过程中希望有人能站出来成为开源项目的领导者，带领大家走出一条别样的路来。</p>
<hr>
<blockquote>
<p>谨把这篇文章献给这两年一起工作过的同事和朋友，我没有留下来什么有意义的 <strong>“胶片”</strong> ，只留下一些产品设计的思路。希望开源基础设施的发展越来越好，成为开源的一个亮点（新星）。并不是 Leader 才需要 Leadership ，希望有一天我能回来讲一个关于开源 Leadership 的新故事。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Git Pack 文件中对象 Header 结构分析]]></title>
            <link>https://maquanyi.com/articles/git-pack-object-header-struct</link>
            <guid>https://maquanyi.com/articles/git-pack-object-header-struct</guid>
            <pubDate>Sun, 21 May 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>在 Git Pack 文件中，每个对象都有一个 Header，Header 的结构如上图所示，由 2 部分组成，分别是对象类型和对象大小。</p>
<p>在第一个 <code>byte</code> 的第一个 <code>bit</code> 是 <strong>MSB（Most Significant Bit）</strong>，只是否继续读取下一个 <code>byte</code>，如果为 <code>1</code>，则继续读取下一个 <code>byte</code>，如果为 <code>0</code>，则表示当前 <code>byte</code> 是最后一个 <code>byte</code>。这个规则就是之前 <a href="/articles/git-pack-object-size-variable-length-encoding">Git Pack 文件中对象长度的变长编码的编解码解析</a> 中提到的 <code>标志位</code> 的编码规则。</p>
<p>接下来的 3 个 <code>bit</code> 是对象类型，Git 中的对象类型有 6 种，分别是：</p>
<pre class="language-c"><code class="language-C">OBJ_COMMIT <span class="token operator">=</span> <span class="token number">1</span>
OBJ_TREE <span class="token operator">=</span> <span class="token number">2</span>
OBJ_BLOB <span class="token operator">=</span> <span class="token number">3</span>
OBJ_TAG <span class="token operator">=</span> <span class="token number">4</span>
OBJ_OFS_DELTA <span class="token operator">=</span> <span class="token number">6</span>
OBJ_REF_DELTA <span class="token operator">=</span> <span class="token number">7</span>
</code></pre>
<p>前 4 种对象类型是 Git 的基本的对象类型，后 2 种对象类型是 Git Pack 文件中的特殊对象类型，分别是 <code>偏移量差异对象</code> 和 <code>引用差异对象</code>。</p>
<blockquote>
<p>后续会继续分析这些内部对象，希望最终能组成一个系列的文章</p>
</blockquote>
<pre class="language-rust"><code class="language-Rust"><span class="token keyword">fn</span> <span class="token function-definition function">parse_object_header</span><span class="token operator">&lt;</span><span class="token class-name">R</span><span class="token punctuation">:</span> <span class="token class-name">Read</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token keyword">mut</span> reader<span class="token punctuation">:</span> <span class="token class-name">R</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token class-name">ObjectType</span><span class="token punctuation">,</span> <span class="token keyword">usize</span><span class="token punctuation">)</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> <span class="token keyword">mut</span> buffer <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    reader<span class="token punctuation">.</span><span class="token function">read_exact</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token keyword">mut</span> buffer<span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> byte <span class="token operator">=</span> buffer<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> object_type <span class="token operator">=</span> byte <span class="token operator">&gt;&gt;</span> <span class="token number">4</span> <span class="token operator">&amp;</span> <span class="token number">0x70</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> <span class="token keyword">mut</span> initial_size <span class="token operator">=</span> <span class="token punctuation">(</span>byte <span class="token operator">&amp;</span> <span class="token number">0xf</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">;</span>

    <span class="token comment">// Recursively read the following bytes if the MSB is set</span>
    <span class="token keyword">if</span> byte <span class="token operator">&amp;</span> <span class="token number">0x80</span> <span class="token operator">!=</span> <span class="token number">0</span> <span class="token punctuation">{</span>
        initial_size <span class="token operator">=</span> <span class="token function">parse_object_size</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token keyword">mut</span> reader<span class="token punctuation">,</span> initial_size<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token punctuation">(</span>object_type<span class="token punctuation">,</span> initial_size<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<ol>
<li><code>byte &gt;&gt; 4 &amp; 0x70</code> 的逻辑是先将 <code>byte</code> 右移 4 位，然后与 <code>0x70</code> 进行 <code>&amp;</code> 操作，这样就可以得到 <code>byte</code> 的 1 - 3 位，也就是对象类型。</li>
<li><code>byte &amp; 0xf</code> 的逻辑是直接与 <code>0xf</code> 进行 <code>&amp;</code> 操作，这样就可以得到 <code>byte</code> 的后 4 位，也就是对象大小在第一个 <code>byte</code> 的值。</li>
<li><code>byte &amp; 0x80</code> 是判断 <code>byte</code> 的 MSB 是否为 <code>1</code>，</li>
</ol>
<p>如果 MSB 为 <code>0</code>，则表示当前 <code>byte</code> 是最后一个 <code>byte</code>；如果是 <code>1</code> 的话就是后面一个 <code>byte</code> 同样是记录对象大小，就需要继续读取下一个 <code>byte</code>。</p>
<p>如果读取第二个 <code>byte</code>，则需要将第二个 <code>byte</code> 中得到的 <code>size</code> 左移 <code>4</code> 位，加入第一个 <code>byte</code> 的 <code>size</code> 进行解码计算； 后面再读取的 <code>byte</code> 时，需要右移 <code>4 + 7</code> 位，加入之前的 <code>size</code> 进行解码计算，直到读取到的 <code>byte</code> 的 MSB 为 <code>0</code>，则表示当前 <code>byte</code> 是最后一个 <code>byte</code>，这样就可以得到对象的大小。</p>
<p>这里需要注意的就是左移位数是 <code>n * 7 + 4</code> ，第一次是 <code>n = 0</code> 移动 <code>4</code> 位 ，然后 <code>n = 1</code> 移动 <code>11</code> 位，<code>n = 2</code> 移动 <code>18</code> 位，以此类推进行计算。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Git Pack 文件中对象长度的变长编码的编解码解析]]></title>
            <link>https://maquanyi.com/articles/git-pack-object-size-variable-length-encoding</link>
            <guid>https://maquanyi.com/articles/git-pack-object-size-variable-length-encoding</guid>
            <pubDate>Fri, 19 May 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Git 使用了一种称为 "变长编码"（<strong>Variable-Length Encoding</strong>）的方案来编码 Pack 文件中的对象大小。这种编码方案的主要优点是它可以用较少的字节来表示较小的数值，同时也能表示较大的数值。</p>
<p>以下是 Git 变长编码的主要规则：</p>
<ol>
<li>每个编码的数值都是由一个或多个字节表示的。每个字节由两部分组成：一个 <code>标志位</code> 和一个 <code>大小部分</code>。</li>
<li><code>标志位</code> 是每个字节的最高位。如果这个字节后面还有更多的字节，那么继续位就是 <code>1</code>，否则就是 <code>0</code>。</li>
<li><code>大小部分</code> 是每个字节的低 <code>7</code> 位。它表示的是编码数值的一部分，最低的部分在前，最高的部分在后。</li>
<li>为了得到编码的数值，需要将所有字节的大小部分合并起来，形成一个二进制数。</li>
</ol>
<p>这种编码方案使得 Git 能够以一种非常紧凑的方式存储对象的大小，特别是对于较小的对象，只需要一个字节就可以表示。同时，它也能表示非常大的对象，只需要增加更多的字节即可。</p>
<p>到这里的时候以为可以自己写出来编码的代码，后来发现还是进行更详细的分析：</p>
<ol>
<li>Git Pack 文件使用变长编码来表示对象大小。这意味着对象大小不定长,可以用 1 个或更多字节来表示。</li>
<li>每个字节的最高位(第 <code>8</code> 位)用于表示是否还有后续字节。如果最高位为 <code>1</code>, 表示后面还有更多字节,如果最高位为 <code>0</code>, 表示这个字节是最后一个字节。</li>
<li>每个字节的低 <code>7</code> 位(第 <code>7</code> 位到第 <code>0</code> 位)用于表示对象大小的一部分。</li>
<li>要编码一个对象大小,我们先将其转为二进制数字。然后从低位开始,每 <code>7</code> 位一组,如果最后不足 <code>7</code> 位,高位补 <code>0</code>。</li>
<li>对每组 <code>7</code> 位,我们设置最高位为 <code>1</code> 或 <code>0</code>, 表示是否还有更多组。如果后面还有组, 最高位设置为 <code>1</code>, 如果这是最后一组,最高位设置为 <code>0</code>。</li>
<li>把这些组合起来,就得到了变长编码后的字节序列。</li>
<li>举个例子,要编码 <code>130</code>, <code>130</code> 的二进制是 <code>10000010</code>。分组后得到 <code>0000001</code> 和 <code>0000010</code> 两组。设置最高位后分别得到 <code>10000010</code> 和 <code>00000001</code>, 转为 16 进制就是 <code>0x82</code> 和 <code>0x01</code>。</li>
<li>所以 <code>130</code> 的变长编码后的字节序列是 <code>[0x82, 0x01]</code>。 <code>0x82</code> 的最高位为 <code>1</code>,表示后面还有字节 <code>0x01</code> 的最高位为 <code>0</code>, 表示这是最后一个字节。</li>
</ol>
<pre class="language-rust"><code class="language-rust"><span class="token comment">//!</span>
<span class="token comment">//! This module provides functions to encode and decode the size of Git objects in a Git Pack file.</span>
<span class="token comment">//!</span>
<span class="token comment">//! In a Git Pack file, the size of each object is encoded in a variable-length format. Each byte of the encoded size</span>
<span class="token comment">//! consists of a continuation bit and 7 bits of size. The continuation bit is the highest bit of the byte, and it is set</span>
<span class="token comment">//! to 1 if there are more bytes of the size. The lower 7 bits of the byte represent a part of the size, with the lowest</span>
<span class="token comment">//! part first.</span>
<span class="token comment">//!</span>
<span class="token comment">//! For example, the size 130 is encoded as two bytes: 10000001 00000001. The first byte has the continuation bit set to 1</span>
<span class="token comment">//! and the size part set to 1, and the second byte has the continuation bit set to 0 and the size part set to 1. When</span>
<span class="token comment">//! decoded, the size parts are combined to form the size: 1 + (1 &lt;&lt; 7) = 130.</span>
<span class="token comment">//!</span>
<span class="token comment">//! The `decode` function takes a reader that implements the `Read` trait, and returns the decoded size. It reads bytes</span>
<span class="token comment">//! from the reader until it finds a byte with the continuation bit set to 0, and combines the size parts of the bytes to</span>
<span class="token comment">//! form the size.</span>
<span class="token comment">//!</span>
<span class="token comment">//! The `encode` function takes a writer that implements the `Write` trait and a size, and writes the encoded size to the</span>
<span class="token comment">//! writer. It splits the size into parts of 7 bits, and writes bytes with the continuation bit and the size part to the</span>
<span class="token comment">//! writer.</span>
<span class="token comment">//!</span>

<span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token punctuation">{</span><span class="token class-name">Read</span><span class="token punctuation">,</span> <span class="token class-name">Write</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// Function to decode the size of a Git object from a reader</span>
<span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">decode</span><span class="token operator">&lt;</span><span class="token class-name">R</span><span class="token punctuation">:</span> <span class="token class-name">Read</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token keyword">mut</span> reader<span class="token punctuation">:</span> <span class="token class-name">R</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">usize</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// Initialize the size and shift variables</span>
    <span class="token keyword">let</span> <span class="token keyword">mut</span> size <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> <span class="token keyword">mut</span> shift <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>

    <span class="token comment">// Buffer to hold the current byte</span>
    <span class="token keyword">let</span> <span class="token keyword">mut</span> buffer <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">;</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token comment">// Loop over the bytes from the reader</span>
    <span class="token keyword">while</span> <span class="token keyword">let</span> <span class="token class-name">Ok</span><span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=</span> reader<span class="token punctuation">.</span><span class="token function">read_exact</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token keyword">mut</span> buffer<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// Get the current byte</span>
        <span class="token keyword">let</span> byte <span class="token operator">=</span> buffer<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token comment">// Update the size by bitwise OR with the lower 7 bits of the byte, shifted left by the shift amount</span>
        size <span class="token operator">|=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>byte <span class="token operator">&amp;</span> <span class="token number">0x7f</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> shift<span class="token punctuation">;</span>
        <span class="token comment">// If the highest bit of the byte is 0, break the loop</span>
        <span class="token keyword">if</span> byte <span class="token operator">&amp;</span> <span class="token number">0x80</span> <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span>
            <span class="token keyword">break</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// Increase the shift amount by 7 for the next byte</span>
        shift <span class="token operator">+=</span> <span class="token number">7</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// Return the decoded size</span>
    <span class="token class-name">Ok</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token comment">// Function to encode the size of a Git object and write it to a writer</span>
<span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">encode</span><span class="token operator">&lt;</span><span class="token class-name">W</span><span class="token punctuation">:</span> <span class="token class-name">Write</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token keyword">mut</span> writer<span class="token punctuation">:</span> <span class="token class-name">W</span><span class="token punctuation">,</span> <span class="token keyword">mut</span> size<span class="token punctuation">:</span> <span class="token keyword">usize</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// Buffer to hold the current byte</span>
    <span class="token keyword">let</span> <span class="token keyword">mut</span> buffer <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">0u8</span><span class="token punctuation">;</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token comment">// Loop until the size is 0</span>
    <span class="token keyword">while</span> size <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token punctuation">{</span>
        <span class="token comment">// Get the lower 7 bits of the size</span>
        buffer<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span>size <span class="token operator">&amp;</span> <span class="token number">0x7f</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">u8</span><span class="token punctuation">;</span>
        <span class="token comment">// Shift the size right by 7 bits</span>
        size <span class="token operator">&gt;&gt;=</span> <span class="token number">7</span><span class="token punctuation">;</span>
        <span class="token comment">// If there are more bits, set the highest bit of the byte</span>
        <span class="token keyword">if</span> size <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token punctuation">{</span>
            buffer<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">|=</span> <span class="token number">0x80</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// Write the byte to the writer</span>
        writer<span class="token punctuation">.</span><span class="token function">write_all</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>buffer<span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token attribute attr-name">#[cfg(test)]</span>
<span class="token keyword">mod</span> <span class="token module-declaration namespace">tests</span> <span class="token punctuation">{</span>
    <span class="token keyword">use</span> <span class="token keyword">super</span><span class="token punctuation">::</span><span class="token operator">*</span><span class="token punctuation">;</span>
    <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token class-name">Cursor</span><span class="token punctuation">;</span>

    <span class="token attribute attr-name">#[test]</span>
    <span class="token keyword">fn</span> <span class="token function-definition function">test_decode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">0x82</span><span class="token punctuation">,</span> <span class="token number">0x01</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token keyword">let</span> cursor <span class="token operator">=</span> <span class="token class-name">Cursor</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token function">decode</span><span class="token punctuation">(</span>cursor<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">assert_eq!</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> <span class="token number">130</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token attribute attr-name">#[test]</span>
    <span class="token keyword">fn</span> <span class="token function-definition function">test_encode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">let</span> size <span class="token operator">=</span> <span class="token number">130</span><span class="token punctuation">;</span>
        <span class="token keyword">let</span> <span class="token keyword">mut</span> data <span class="token operator">=</span> <span class="token class-name">Vec</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">encode</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token keyword">mut</span> data<span class="token punctuation">,</span> size<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">assert_eq!</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">0x82</span><span class="token punctuation">,</span> <span class="token number">0x01</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>对于解码，我们按照之前的逻辑再逆向分析：</p>
<ol>
<li>当读入一个 <code>byte</code> 的二进制是 <code>10000010</code>，首位是 <code>1</code> 说明还需要读取后面的一个 <code>byte</code> 。这里把这个字节的后 <code>7</code> 位存下来，就是 <code>0000010</code>。</li>
<li>继续读下一个 <code>byte</code> 的二进制 <code>00000001</code>，首位是 <code>0</code> 说明长度读取已经结束了。 把这个字节的后 <code>7</code> 位和前面的 <code>7</code> 为拼接在一起，形成 <code>00000010000010</code></li>
<li>前面补 <code>0</code> 去掉得到 <code>10000010</code> 的二进制，转换为十进制得到 <code>130</code></li>
</ol>
<p>计算为长度后，之后流中 <code>130</code> 个 <code>byte</code> 就是这个对象的数据了。 通过这种方式当 Git Pack 以流的方式传入，就可以按照顺序读取解析出所有的对象。当然 Delta Object 和 Reference Object 对象还需要更详细的解析，</p>
<blockquote>
<p>希望酒后再失眠的时候能和 AI 机器人们聊清楚 Delta 和 Reference 两种对象解析。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[《Go 并发编程实战》第二版 - 序]]></title>
            <link>https://maquanyi.com/articles/go-book-perface</link>
            <guid>https://maquanyi.com/articles/go-book-perface</guid>
            <pubDate>Sat, 18 Mar 2017 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>收到郝林邀约为《Go 并发编程实战》第二版写一篇序的时候，正好在湾区参加 Google NEXT 2017 大会。偶遇到《Go In Action》一书的中文译者李兆海从慕尼黑到湾区出差，他从七牛加入到 Google 已接近一年的时间。遂约他在 Google 的腹地 Mountain View 小聚，想听听他从国内 Golang 语言重度使用的公司跳槽到 Golang 语言发源的公司之间的经历和感受。</p>
<p>然而席间讨论最多的不是 Golang 在 Google 的使用和发展，而是对 Google 工程能力的感慨。从回归测试、应用部署或是系统伸缩，无一不是配置几个参数，动动鼠标就能完成，和国内很多公司动则人拉肩扛搞系统上线或跪拜原始繁重的 IPD 流程真是天壤之别，让我这样在也号称是国内大厂工作的人汗颜。其背后先进的工程理念和开放的工程师文化，无一不推动 Google 这架巨大的商业机器缓慢前行，虽然每次的改动改动都看似微乎其微，但汇聚在一起形成的动力是澎湃和持续的。</p>
<p>这样公司生产的新一代编程语言，设计中无不透露这工程化的气息。Golang 每个版本发布的新特性看似都十分微小，但每隔半年就有一个 major version release 到社区，包含了众多的 Bug Fix 和特性，当回头看看 1.0 到 1.8 版本的变化，发现 Golang 已经成熟了很多，已然能够担当我们工作中的主要编程语言，这种发展节奏的控制也正是 Google 工程理念和文化的缩影。又如 Golang 的 Core Team 一直跟随着 HTTP/2 的草案，不停的在版本中发布特性支持，在最新的 1.8 版本中终于加入了 Push 特性的支持，这一步一步的进展也正是跟随 HTTP/2 在业界推进脚步，同时加入了 HTTP Tracing 的支持更是工程化中实用性的表现。</p>
<p>接触 Golang 语言还是在 2013 年开始研究 Docker 的时候，Docker 在开源之前把内部的 Python 版本用 Golang 语言重新进行了实现。从 Docker 击败 LXC 成为容器领域的事实标准开始，到现在 Kubernetes 成为 Cloud Native 编排首选，Golang 语言成为 Cloud Native 领域的基石，因此许多人把 Golang 语言定义为新一代云计算的 “C” 语言。在容器技术已经悄然落地成为软件基础平台的首选、微服务大行其道的今日，我更愿意 Golang 能够替代 Java ，简洁的语法、清晰的结构、高效的并发、完整的工具链都让 Golang 成为企业级开发的首选语言成为可能。</p>
<p>很多人认为 Golang 语言的成功是源于某些项目的成功，其实真正动力来源于社区。在中国有一群积极的人在推动 Golang 社区的发展，像国内最早的 Golang 语言布道者许世伟、积极组织 Gopher China 大会的谢孟军、贡献 Codis、 TiDB 这样的优秀 Golang 开源项目的刘奇，让人汗颜的是我只是在一些会议和 Meetup 活动中进行过宣讲。个人最佩服的还是坚持组织 Golang Meetup 并写书的郝林，能把本书更新到第二版着实不易。第二版相比第一版在语法基础部分做了删减，并根据 Golang 新版本的变化做了更定。这符合当前国内社区的状况，Golang 已经在很多公司和很多优秀的项目上落地，开发者更多需要实践的指导。</p>
<p>正是这些社区的力量让中国成为 Golang 语言发展最快和热度最高的国度，真诚的感谢为社区发展贡献力量的布道者、活动组织者、Maintainer、Contributor 、作者和出版社，是你们让 Golang 社区在国内蓬勃发展成为可能，希望本书的广大读者能积极参与社区的讨论、活动，贡献更多优秀的开源项目。维护一个健康的社区对于社区中的每个人都是非常有利的。</p>
<p>Thanks，</p>
<p><em>Hammersmith, London</em></p>
<p>18-03-2017</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[We Have No Moat, And Neither Does OpenAI - 巨头、开源和创新，谁到底是赢家？]]></title>
            <link>https://maquanyi.com/articles/google-we-have-no-moat-and-neither-does-openai</link>
            <guid>https://maquanyi.com/articles/google-we-have-no-moat-and-neither-does-openai</guid>
            <pubDate>Fri, 05 May 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>对于 AI 我从来没有研究过，只是 ChatGPT 的用户而已，对于 AI 发展的判断我更是个小白。但是 Google 这篇 “We Have No Moat， And Neither Does OpenAI” 文章的中几个观点是非常有意思，我也想说几句表达一下个人的看法。</p>
<blockquote>
<p>当然我就是个蹭热点的，不要把我的话当真，当真你就输了，😂</p>
</blockquote>
<p>没想到 Google 这 <strong>“浓眉大眼”</strong> 在开源、技术创新等任何方向上都举足轻重的科技巨头，也在为开源还是不开源、生态到底能不能颠覆经侦对手而争论。我以为这种场面只会发生在菊厂的胶片里，真是没有想到还有一天能看到这样的文章。这内容一定会被国内各大 OSPO 拿来证明开源 <del>自己存在</del> 的价值，当然更多 VC 也会引用文章内容成为投资的理论依据。</p>
<p>按照文章的观点 OpenAI、Meta 还是 Google ，这场 AI 大战里面谁能获胜，取决于谁能开源谁获得生态主导权，才能在战火中笑到最后。不懂 AI 所以其实我也没有任何判断的能力，以下观点仅依据于我的工作经历，</p>
<ol>
<li>Docker 之所以在云计算时代成功，是把内核技术（namespace 和 cgroups）通过一个 Image、Distribution 和 Dockerfile 等产品手段妙降低了使用门槛，到今天写 Dockerfile 已经成为开发者的基本技能。从这个角度看，能把从事 AI 开发或者使用的门槛降到最低，又能领导生态发展方向的厂商才是最后的王者，这就必须具备几个条件了：</li>
<li>开源是最大限度最低成本吸引广大开发者的方式，关键是看底谁会真正的先开源？OpenAI 现在的状况就是这个领域的 AWS ，绝对江湖大佬，众多人的衣食父母。但是开发者想在 OpenAI 的生态中活下去，既要有自己的产品特点、还要和别人竞争，最后还要看 OpenAI 的脸色。开发者都是生态下的蝼蚁而已，活着肯定是没有问题，但是想活的好就要绞尽脑汁在应用层面创新了。Google 要想翻盘只能开源，就需要把之前 Android 和 Chrome 的策略再演一次，从文章来看 Google 就在这决策关键时间里吧，在 AI 这个领域能不能复制 <del>翻盘</del> 就不得而知了，这需要要懂 AI 的专家 <del>砖家</del> 出来给结论。但是 Meta 是一家奇怪的公司，他们的开源项目从来都是非常有竞争力，但也从来不做开源项目的 2B 或者 2D 生意，按道理他们把 AI 在自己的生态上应用起来，会有不错的收益。现在的开源偷跑就搅得整个生态混浊不清，看不出谁是未来的技术领导者。这里我赌 Meta 会是这个领域开源的急先锋，这符合他们一贯的方式。</li>
<li>这里还是有一家 AI 领域 “Docker” 公司的机会，把参与 AI 的门槛拉到最低，让任何一个小白开发者都可以上手。我不懂 AI ，目前层出不穷的各种公司里面我也看不出来谁有这样的潜力。到底谁能脱颖而出，还需要时间吧。不知道各家风投是不是都已经买定离手了，如果还没有赶紧下注啦。如果 OpenAI 自己下场，那别人也就没有啥玩的机会了。</li>
<li>说回来 OpenAI、Google 和 Meta 三家公司，当然可以把 Microsoft 看做上 OpenAI 后面的靠山，我倒是觉得谁如果能生产硬件，并且把硬件和自己的大模型、应用以及生态打通，降低 AI 成本或者小型化，那有可能在这场军备竞赛中笑到最后。如果 Nvidia 买下 OpenAI ，那这场面就壮观了。这观点可能不怎么正确，也许是在菊厂工作受到的影响吧。</li>
<li>感谢 AI 的创新，让我找到了从事技术工作的一个新乐趣。和 ChatGPT 聊天已经让我觉得刷抖音是很无聊的事情，我坚信未来每个人都有自己的 ”<strong>J.A.R.V.I.S</strong>“ 。（请自行脑补天津方言版 <strong>J.A.R.V.I.S</strong> 的发音...）</li>
</ol>
<p>P.S.</p>
<p>上个月跟一个在 Google 工作的朋友吃饭，从聊天中感觉 Google 已经很久不知道自己应该做什么了，现在有了动力不知道这艘 “航空母舰” 会不会上个 “核动力” 的芯再次远航？这些年工作得益于使用 Google 搜索各种信息，快速学习各种新技术，对这家公司还是有些感情，希望能它不要在这个技术 “奇点” 的关键时刻掉链子。 另外建议大家看一下原文上面那段小字，这个文档应该不是 Google 内部的结论，看起来应该是一个讨论里面的 notes ，代表了 Google 部分人的观点。坐等 Google 出来辟谣 😂</p>
<blockquote>
<p>从心里我肯定是希望各家的大模型都开源，不开源哪里能有 ”自主可控“ ？没有 “自主可控” ，解决 “掐脖子” 问题就比较难了。</p>
</blockquote>
<h3>原文链接</h3>
<p><a href="https://www.semianalysis.com/p/google-we-have-no-moat-and-neither">Google: We Have No Moat, And Neither Does OpenAI</a></p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[2023 年 KCD 大连站活动后记 （2023-04-15）]]></title>
            <link>https://maquanyi.com/articles/kcd-dalian-2023-04-15</link>
            <guid>https://maquanyi.com/articles/kcd-dalian-2023-04-15</guid>
            <pubDate>Sun, 16 Apr 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="2023 年 4 月 15 日 大连 KCD 活动" loading="lazy" width="1702" height="1276" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FKCD-dalian.cf3fd87b.jpeg&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FKCD-dalian.cf3fd87b.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FKCD-dalian.cf3fd87b.jpeg&amp;w=3840&amp;q=75">
<blockquote>
<p>只有这样的安排，才是 “真” 的开发者社区活动，茶歇一点都不能激起交流的欲望。啤酒是 Social 的源动力，当然现在可以加上 Club Mate 佐饮，更显得 Hacker 风范。</p>
</blockquote>
<img alt="张晋涛" loading="lazy" width="5472" height="3072" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fzhang-jintao.5221f5b4.jpeg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fzhang-jintao.5221f5b4.jpeg&amp;w=3840&amp;q=75">
<p>上午赶到会场的时候演讲已经开始，我进屋真正坐下来听的时候已经错过了移动的安全议题开头，后面议题是 <a href="https://twitter.com/zhangjintao9020">张晋涛</a> 的演讲， 其中对于 etcd 的加解密存储讲得很细致，还有一些性能数据做了对比。 对于 Kubernetes 的安全问题，从里到外，从上到下都是攻击面，我心里更想听的是一个完整的解决方案介绍。不过就是有这样的方案，也未必能卖钱。</p>
<img alt="云原生喝酒 SIG 主理人试饮" loading="lazy" width="2268" height="4032" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdustise-beer.ab65a733.jpeg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdustise-beer.ab65a733.jpeg&amp;w=3840&amp;q=75">
<blockquote>
<p>茶歇阶段的重头戏必然是本次活动的饮品，备受东北开发者好评的软饮，新鲜罐装贴有云原生喝酒 SIG Logo 的啤酒，可以从 SIG 主理人璀璨的笑容中感受到这次活动的品味，这才是 “真” 开发者活动应该有的样子。</p>
</blockquote>
<img alt="一图流" loading="lazy" width="2048" height="1536" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fone-picture.f72dedd7.jpeg&amp;w=2048&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fone-picture.f72dedd7.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fone-picture.f72dedd7.jpeg&amp;w=3840&amp;q=75">
<p>接下来就是我的演讲环节，本来想放弃演讲的，但是受到现场气氛的感染，决定还是把原计划讲的内容 “唠一唠”。因为没有时间写 <del>PPT</del> 胶片 ，就打算上台 <del>裸奔</del> “裸讲” ，还是 <a href="https://twitter.com/majinghe1">小马哥（马景贺）</a> 现场临时把演讲题目做一页标题就这样开开始了。</p>
<p>现在我其实已经不大记得当时讲了啥，只是依稀记得把要介绍的几个项目都说了一下，表达一下自己的观点，其实也就一条：<strong>Rust 可能会在云原生领域偏底层操作系统的范围有所创新</strong> 。 但是现场忘记聊 WebAssembly 这个事情，估计是被 “小马哥” 举牌示意我超时打断了 <del>忽悠</del> "唠嗑“ 的节奏，如果有 <del>胶片</del> PPT 的话可能就会比较不容易忘记。</p>
<blockquote>
<p>感觉非常对不起大连的开发者，大周六的赶来以为是听技术议题，结果碰上一个 “脱口秀” 风格的讲师，还连累超时影响吃午饭。</p>
</blockquote>
<img loading="lazy" width="5472" height="3072" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgusiwei-1.1cd9f5a6.jpeg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgusiwei-1.1cd9f5a6.jpeg&amp;w=3840&amp;q=75">
<p>而后听 <a href="https://twitter.com/wey_gu">古思为</a> 介绍了图数据库，对于这个领域我从来没有接触过，并不像 SQL 、NewSQL 数据库那么普及，这个方向以后应该多关注。</p>
<img alt="云原生喝酒 SIG 主理人演讲" loading="lazy" width="5472" height="3072" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdustise-topic.6a8a3db4.jpeg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdustise-topic.6a8a3db4.jpeg&amp;w=3840&amp;q=75">
<p>最后一个议题我就到另外一个会场去看 <a href="https://twitter.com/dustise">老崔</a> 的 Show，我知道他一般喝两杯讲起来就发挥得特别好。开场自我介绍的时候他的介绍很感人，<strong>“我目前最主要的 Title 就是云原生喝酒 SIG 的群主”</strong> (本群聊天已经炸了好几个腾讯会议的号了，老崔没有被叫去 “喝茶” 还公开演讲已经是非常不容易了)。 <a href="https://twitter.com/dustise">老崔</a> 讲什么已经不重要了，关键是他的 PPT 风格没有被 “胶片” 影响，依旧保持开发者的风范。</p>
<p>下午的活动没有怎么参与，回到酒店休息。晚餐必须是热闹非凡的东北烧烤，当然最后一场必须是在精酿酒吧结束。云原生喝酒 SIG 主理的活动，这个流程就必不可少了。</p>
<img alt="云原生喝酒 SIG" loading="lazy" width="4096" height="2304" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcloud-native-beer-sig.e3337b1a.jpeg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fcloud-native-beer-sig.e3337b1a.jpeg&amp;w=3840&amp;q=75">
<blockquote>
<p>真正的后记来了，从离开腾讯后确实没有怎么关注云原生领域的发展。从当前的感觉来看，这个领域已经没有特别地创新。不知道是真的进入落地阶段，还是已经进入自嗨、回魂还是僵死。正好第二天转机到阿姆斯特丹参加 KubeCon ，是一个近距观察的机会。当前确实除了 ChatGPT 这一波，其他的领域都比较沉寂。 这也是云原生喝酒 SIG 在香港筹建了 Web3 Infrastructure Foundation 的原因，未来的开源和创新也许会在这个领域出现。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[马道长和开源之夏的故事]]></title>
            <link>https://maquanyi.com/articles/me-and-ospp</link>
            <guid>https://maquanyi.com/articles/me-and-ospp</guid>
            <pubDate>Wed, 27 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="开源之夏 2024" loading="lazy" width="1792" height="1024" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsummer.58a93a72.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsummer.58a93a72.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fsummer.58a93a72.png&amp;w=3840&amp;q=75">
<p>我可能是唯一一个作为活动组织方、参与社区负责人和项目导师三重身份参与过历届开源之夏活动的人，对于开源之夏的感受(感情)颇深。 这样一个大型的开源活动能坚持至今实属不易，各种辛酸只有经历过的人才知道，写一些想法希望开源之夏越办越好。</p>
<hr>
<p>2020 年我回到华为负责 openEuler 社区的运营，这时候中科院软件所找到我希望和 openEuler 社区开始合作举办开源之夏活动，当时想法也比较简单，是希望参照 Google Summer of Code（GSoC） 的模式针对中国高校学生开展参与开源社区的培养，毕竟国内的学生想参与 GSoC 的难度非常搞。万事开头难，在开源社等国内一众活跃社区的支持，活动还是启动起来。但是理想和现实之间的差距还是有很大，经过几年的摸索逐渐成型。从默默无闻到成为国内最具有影响力的开源编码活动，我个人最敬佩的是软件所同事始终坚持初心，把教育高校学生参与开源社区作为活动的第一位，努力培养国内开源生态发展的基石。 这份初心保护了活动始终坚持在自己的道路上前行，没有盲目扩大，始终把学生培养和项目质量放在首位。</p>
<p>当然逐渐国内也出现了一些模仿的活动，很多类似的活动都有背后自己的目的，有的是为组织方赚声誉的，有的是为了推自己代码托管平台的，当然更多的是带着商业目的在里面。能坚持初心做好一件事情实属不易，中国科学院软件研究所算是当前国内开源生态中的一股清流，成为国内开源社区的一面旗帜和中流砥柱。当然还有 “一生一芯” 等活动都搞得相当不错，清华大学陈渝老师每年的操作系统竞赛和训练营也是搞得风生水起，相比其它高校的竞赛让人真心觉得国内计算机教育和科研的落后是有原因的。</p>
<hr>
<p>作为参与社区的负责人，我觉得开源社区有这样活动的机会非常难得，但一定要正确看待活动给社区带来的价值。 参与开源之夏，并不是要借此机会找到免费的劳动力完成社区项目中某个特性的开发，而很多社区的参与者都是这样想，也有把它作为完成社区运营指标的一个方式这更让人心痛。我觉得作为项目方是要利用活动的影响力找到对社区项目感兴趣的学生，让学生能够借此机会和社区有更充分的接触，在完成社区任务的同时和社区建立起感情，能在活动结束后留下来成为社区的中坚力量。社区（Community） 是由人组成的，更多有共同兴趣的人长期聚合在一起才能形成社区发展的动力。Linux 社区也再尝试引入 Rust 语言，希望能够吸引更多的年轻人参与到社区中来是其中一个重要的原因。</p>
<p>所以对于社区的导师我有几个建议，</p>
<ol>
<li>不要在意学生简历中所在学校是 985 还是 211，还有一些无谓竞赛的成绩，目标是找到合适的人。 选择学生之前，最好导师和每个同学开一个小时的会议， 让学生充分介绍一下自己，挑一个对技术感兴趣、有学习能力、爱分享和有热情的同学。开源之夏活动后，这样学生有希望留下来成为社区的一个长期贡献者。 当然不是每次都能看对人，但是每年都有一个选择的机会。</li>
<li>在开发的过程中，每周和同学通过会议进行沟通。 根据我的经验，每周抽出一个小时和同学开个视频会议即可。让参与的同学感受到导师有真正的辅导， 而不是在任务最后只 Review 提交的代码。 只有人与人真正的交流，才能让同学感受到开源社区，才能有机会让学生留下来成为长期贡献者。社区（Community）是人和人的交集，只有交流才能让学生真正理解开源、社区这种开发形式的魅力。</li>
<li>既然是导师，就有培养学生的目标，不要把参与项目的同学当做是外包。 要把方案的设计权交给学生，让他们有 Research 的机会， 让学生提出解决思路，通过充分的讨论确定方案。让他们通过这次活动掌握如何学习和解决问题的方法，成长为一名合格的开发者，而不是一个完成任务的机器人，要让学生感受到导师的指导和信任。</li>
<li>因为 3 ，项目的任务要有一定的难度，简单的任务并没有必要放在开源之夏活动中。 在项目的开发目标中分解出一个有意义、有难度和需要探索的任务，这样的任务更合适培养学生参与。 即使最后同学没有完成任务，参与其中的学习过程也会其对成长有非常大的帮助。教书育人，育人远比教书更重要。</li>
</ol>
<hr>
<p>作为项目的导师，也有一些话想对参与的同学讲，</p>
<ol>
<li>开源之夏是不是一个竞赛。如果有明确的就业和升学目标的同学，参与开源之夏是性价比非常低的。 去年我在南京大学参加 CCF 开源校园行的活动中，我非常认可其中一位老师的观点，参与开源对于一些名牌学校学生来讲是性价比非常低的事情，对就业和升学都没有明确的帮助。也许这也是高校老师少参与开源社区的原因吧，对于其发论文拿项目没有啥帮助。</li>
<li>开源之夏是适合大三、研一的同学参与。大三如果确定不考研，希望能早点接触到这个行业，开源之夏是非常好的一个渠道，有机会通过开源社区同行业中的精英人群接触，对于今后的发展是非常有价值；对于研一的同学， 已经有自己的科研方向，在学习和科研任务完成有余力的情况下最适合参与开源之夏活动， 可以在目标的领域里面寻找合适的项目和社区，通过更多的接触实际生产了解行业或产业的发展。</li>
<li>开源之夏是一个学习的机会，要利用这个机会培养自己学习和研究问题的能力，而不是当做一个竞赛，拿名次得奖金。 要提出解决问题的思路，根据导师的建议学习相关的知识，掌握学习方法培养解决问题的能力才是最关键。</li>
<li>开源之夏是一个培养社交能力的机会。在一个开源社区里面的发展有很多基本技能，譬如正确的编写文档，简洁的描述问题，面对面沟通技巧等都是可以通过和导师的活动学到很多。</li>
</ol>
<hr>
<p>今年我依旧作为导师参与，当然前期筹划的时候我也有一些参与。希望能继续和同学一起在 <a href="https://github.com/web3infra-foundation/mega">Mega</a> 项目上做一些有意思的尝试。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[开源社区与运营 - 2021 上海计算机教育年会演讲稿]]></title>
            <link>https://maquanyi.com/articles/open-source-community-and-operating</link>
            <guid>https://maquanyi.com/articles/open-source-community-and-operating</guid>
            <pubDate>Thu, 03 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>大家好，我是马全一，目前在华为技术有限公司工作，任鲲鹏开源社区运营总监，主要负责 openEuler 操作系统的开源社区运营，同时也负责 openGauss 、openLooKeng 两个开源项目的运营指导。2000 年参加工作后一直从事技术开发和技术管理等工作；2013 年开始在国内运营 Docker 中文社区，推广容器技术、Kubernetes 等云原生技术；2015 年加入华为负责 CNCF 等海外基金会的一些工作，维护华为和谷歌、红帽等云原生领域核心企业的开发者关系，同时也参与了华为云容器相关产品的设计和开发；2017 年底离开华为加入腾讯，负责腾讯云容器产品的设计，开源项目 TKEStack 项目的产品设计和开源工作；2020 年初又回到华为负责 openEuler 操作系统开源社区相关的运营工作。因为工作的原因，这两年也致力于同一些高校操作系统课程的老师合作进行教学改革的探索。</p>
<hr>
<p>今天和大家主要聊的话题是什么是开源社区以及开源社区和高校的关系等几个方面。在这两年的工作里面，我发现大家对开源、对社区这两个词都有一定的误解，所以今天也是借这机会跟各位老师一起探讨这个非常有意义的话题。</p>
<p>最近这两年 “<strong>开源</strong>” 这个词逐渐进入到大家的视野，尤其是最近政府把开源写入了十四五的规划中，大家更加迫切的去思考这个词等含义和如何做到 <strong>“开源”</strong>。</p>
<p>十四五规划在 <em><strong>第一节 加强关键数字技术创新应用</strong> 中写了如下话：</em></p>
<blockquote>
<h4>聚焦高端芯片、操作系统、人工智能关键算法、传感器等关键领域，加快推进基础理论、基础算法、装备材料等研发突破与迭代应用。加强通用处理器、云计算系统和软件核心技术一体化研发。加快布局量子计算、量子通信、神经芯片、DNA存储等前沿技术，加强信息科学与生命科学、材料等基础学科的交叉创新，支持数字技术开源社区等创新联合体发展，完善<strong>开源</strong>知识产权和法律体系，鼓励企业开放软件源代码、硬件设计和应用服务。</h4>
</blockquote>
<p>这周我也在 《<em><strong>工业和信息化部关于印发“十四五”软件和信息技术服务业发展规划的通知》</strong></em> 看到了 ”<strong>开源</strong>“ 这个词反复出现，在整个通知中提到 ”<strong>开源</strong>“ 有 40 多次。相信教育部等相关部委也会根据十四五规划的精神发布者自己的规划，可以预计 ”<strong>开源</strong>“ 一定会出现在各种文件中。在国家的文件中，我们可以看到 ”<strong>开源</strong>“ 是作为一种软件开发的行为出现，是一种软件开发的模式，从封闭的模式到开开放的模式。</p>
<p>当然我们也经常看到国外的一些文章介绍开源，国内甚至出现了一些人称自己是 “<strong>开源布道师</strong>” 甚至是 “<strong>斗士</strong>”。从这些人的口中讲出来的开源，更像是一种文化现象，是一种软件开发的指导思想。这让我想起来这些年在软件开发行业经常讲 “<strong>DevOps</strong>” 这个词，现在大多数软件公司的软件开发和运维都尽量遵循 “<strong>DevOps</strong>” 。但是 “<strong>DevOps</strong>” 是没有一个固定规则的，我们看到的一些讲 “<strong>DevOps</strong>” 的文章也更多是一种最佳实践的总结，所以这些总结都是根据公司的业务发展、技术演进逐渐演化而来的，用一个更加学术的点的词形容 “<strong>DevOps</strong>” 是形而上学。从非官方角度听到的国内外 <strong>“开源”</strong> 和 "<strong>DevOps</strong>" 更像一些，也是一种形而上学的指导。</p>
<p>那 <strong>”开源“</strong> 到底是什么呢，其实也根本没有一个官方的定义。最近我也有幸参与了信通院、工信部等部门关于 <strong>”开源“</strong> 的相关标准制定，在各种讨论中来自于各个公司和社区的专家也有各种争论，我看大家一时也不会达成一种共识。当 <strong>”开源“</strong> 对于软件开发影响越来越大，当 <strong>”软件吞噬世界，开源吞噬软件“</strong> 这样的论点层出不穷时，就会引来一些主管机构的忧虑，当然就有必要制定标准。制定标准的目的在我看不仅是了发展 <strong>“开源”</strong>，更多的是管理 <strong>”开源“</strong> 这种行为。当然也参照国外的做法，成立了政府领导的非盈利组织开源基金会，这是一个进步，当然也是一个困扰，国内早就有不少非盈利组织承担某些政府职能造成的负面新闻。</p>
<p>从我个人来看更觉得 <strong>”开源“</strong> 是一种文化，是一种文化来指导工程师的软件工作，就像 <strong>“DevOps"</strong> 指导大家的开发过程更加敏捷和高效一样。如果参与者没有 ”<strong>开源</strong>“ 的精神，那 <strong>”开源“</strong> 就只是一种动作，把代码开放出来让人访问而已，可能这也是会有 <strong>”开源运营“</strong> 这个工作职位到原因。代码开放出来需要有人访问，需要有人去用，需要有人参与到开源项目中贡献，那怎么办呢？找个人来做这个运营的工作，把社区盘活吧。谁想到这个职位还挺热的，最近每周都会有不同的猎头加我的微信问我某某公司的开源项目是不是感兴趣。</p>
<p>所以 ”<strong>开源</strong>“ 是一种文化和行为的复合体，是无法没有一个具体标准的行为。所以我想现在看到的国内 ”<strong>开源</strong>“ 更多是一种行为， ”<strong>开源</strong>“文化对于本来已经博大精深的中国文化来讲，融入也就需要漫长的时间，也许更多的是将来会是有一种 <strong>“中国特色”</strong> 的 “<strong>开源</strong>” 。当然这不是我们的独特性，在美国和欧盟也都是有很多 <strong>“开源”</strong> 被政府、军方进行管理和指导。</p>
<hr>
<p>提到 <strong>“社区”</strong> 这又是一种纠结的词需要解释。首先大家提到社区就会想到我们居住的小区，实际在 Community 这个词的翻译成 <strong>“社区”</strong> 后首先用到了我们居住地，真正对于文化这个方向， <strong>“社区”</strong> 这个词实际用的比较少，其实 <strong>“社区”</strong> 是没有物理、时间和空间限制的。</p>
<p>我在运营工作中最被经常被问的问题是如何加入 openEuler 社区？在当前我接触的人里面，大多互联网经历是从注册论坛开始的，其实我上学的时候也会跑到水木清华上求各种下载资源的链接。所以在大多数人的眼里，加入社区就是要去个论坛中注册，如果没有注册过程那就不叫加入社区。一般解释到这里的时候，我脑中就会出现电影或电视情节中普遍的场景，大学生穿着麻布 Hoodie ，拿着蜡烛口念咒语加入兄弟会。</p>
<p>跟 openEuler 的操作系统发行商解释什么是 <strong>“开源”</strong>、什么是 <strong>“社区”</strong> 还好，这正好是我的工作，如果大家都懂开源，我也就失业了😄，只能回去写代码搞产品设计了。但是公司也需要让我讲明白如何加入到 openEuler 社区，因为公司有一些其它的组织同时也在搞技术社区，方法就是弄了一个论坛，只要在论坛注册了就是加入了这个技术社区，不管多少方法总是可以搞几十万注册用户的，也就是几十万人加入了某技术社区。这给老板汇报的时候听起来多有气势和价值，所以我的职级徘徊不前是由原因的。当然还有某友商搞的 AI 开源框架，技术做的确实不错，但说起来社区都敢讲百万了，更让我无地自容，愧对公司给我的薪水了。</p>
<p>为了应对公司的纠结，openEuler 以签署 CLA 作为加入社区的条件。CLA 是 <strong>Contributor License Agreement</strong> 的简称，中文称为 <strong>贡献者许可协议</strong>，其实这是一些大型项目为了在法律诉讼中免责而让开发者签署的一个文件，跟社区其实没什么因果关系。因为 openEuler 设计的 CLA 签署环节中，企业签署需要加盖公章，一般公司都会有个相对比较正式的流程，有法务审核，如果签署的话，是对 openEuler 社区的认可，所以作为该公司加入 openEuler 社区这个说法来看，<strong>::于情::</strong> 是可以讲的过去。所以公司动用各种部门的力量，在短短两年的时间里面签署了 300 多家公司，而且在当前这个政治形势下，还有不少欧美公司签署，确实是国内执行力最强的公司。只是 <strong>::于理::</strong> 来讲，这和加入社区没有什么关系。</p>
<p>对于 <strong>“社区”</strong> 的真正含义，只要是大家对共同的事务感兴趣就可以说是 <strong>“社区”</strong> 的一份子了。即使我是使用 openEuler 操作系统的用户，没有跟社区有什么交集，都可以说是社区的成员。所以这里关键点是对共同的事务感兴趣，这里的事务可以是实体的也可以是非实体的。所以从形式看，我们可以用另一个词来形容 - <strong>Club</strong> 。当然这个 <strong>Club</strong> 不是夜店的意思，而是指对某件事情感兴趣的人聚集在一起而形成的团体。我和几个做云原生相关开发的开发者朋友，因为大家都对某种酒类感兴趣，所以在去年（2020 年）疫情的时候组建了一个 <strong>”云原生喝酒 SIG “</strong> ，这里有人喜欢白酒、有人喜欢啤酒，我更喜欢精酿啤酒。其实这就是一个 Club 的形式，我们也可以看作是一个社区，我们经常半夜聚集在一起，通过腾讯会议大家各自拿出藏酒，边喝边讨论技术问题或者各种八卦消息。在我看这是一种非常让人舒服的社区形式，没有必要大家一起签个文件什么的，最近这个社区酒后已经膨胀到要去注册一个 <strong>”云原生喝酒基金会“</strong> 来正式运作几个开源项目了。</p>
<p>所以社区是一群人因为共同的兴趣和爱好，不论时间、空间、年龄、性别、国际、种族等等聚集在一起形成的一种聚集形态。当然可以给这种形态设计一个门槛，签署一些合同或者文件，或者其它任何形式的仪式。</p>
<hr>
<p>当我们把 <strong>“开源”</strong> 和 <strong>“社区”</strong> 两个词叠加在一起，就得到了一个组合。华为内部有个黑话可以解释这种组合，这个词就是 <strong>“正交”</strong> 。对于 openEuler 社区来讲，这个共同感兴趣的实体就是 openEuler 开源操作系统，这个社区就是属于下载安装使用的那些开发者、在 gitee 上提交代码的开发者、参加社区会议的开发者、还有那些看到 openEuler 各种宣传但因为拖延症晚期还没有去尝试更深入了解的人。我的工作就是让这些人能够在社区产生更深层次的活动，产生新的想法和 Idea 并且鼓励大家把它变成现实。听起来这份工作是让人心悦神奕的，其实在工作中也是磕磕绊绊一地鸡毛。</p>
<p>我在公司内外最经常讲的话是 <strong>“每个人都是趋利”</strong> 的，对于我自己也是这样。所以这样一个面向服务器的操作系统，大多数用在数据中心安装的操作系统，实在是无法激起那些已经每天 996 、007 的开发者去尝试使用。不管我们提供多少听起来让人热血沸腾的技术特性，对于开发者来说并不能影响他们眼前的工作，也无从有动力做这个尝试。操作系统可以说是这个行业最古老的存在，技术代差以数十年记，在当前互联网和智能设备普及的今天，是拿不出来一个什么特性吸引普通开发者去尝试的。不光是华为，腾讯阿里这些友商的竞品也跟我们面临了同样的问题。</p>
<p>但是华为有自己的战略决心，我们会把精力投入到下一代开发者的培养上，这是其它公司的在这种重投入，长周期和低回报的项目上最终会被甩在华为身后的原因。这是大家这两年在华为参与、主导的跟各个高校合作上，除了之前的各种合作方向之外，操作系统也逐渐增加比重的原因。但是我也看到的是高校的计算机教育上，从事基础软件不光是操作系统，包括数据库的教授和老师是少之又少的。当然这也是 <strong>“趋利”</strong> 的结果。但是华为有信心更有耐心，会在基础软件这个方向持续投入，为整个行业培养培养未来的基石人才。</p>
<p>我们这两年也在尝试和高校从事教学改革之外的合作方式，譬如我们正在和大连理工大学软件学院在开源合规、操作系统、<strong>Rust 编程语言</strong>等方向展开更多的合作。由华为的技术专家和大学教授一起组建开源实验室，辅导学生在各种前沿的技术方向上投入研究。当然这种研究的基础是必须是开源，必须是在 openEuler 社区内进行。我们采用这样开放的研究方式，希望能够尝试把开源社区构建成一个开放的科研平台，聚集不同学校感兴趣的老师一起深入探索。我们希望把开源的文化精神范围扩大，扩大到学术研究的方式上。这件事情给我带来的愉悦感，远远大于给各种企业讲解如何加入开源社区来的更多。</p>
<p>openEuler 开源社区和中国科学院软件研究所深入合作了 2 年，我们共同执行的 <strong>暑期 2020</strong> 、<strong>暑期 2021</strong> 是这两年国内开源社区最受瞩目的项目，通过这两年的暑期活动，有近千名高校学生有机会通过开源社区和业界顶尖的工程师合作、学习。最近我们又设计了新的 <strong>openEuler 开始社区实习</strong> 项目，让这种方式从暑期的时间延续到在校的其它时间。学生加入到实习项目后，可以在开源社区领取实习的项目，和华为以及其它合作伙伴的工程师一起学习工作，完成项目后会有相应的积分，获取一定积分就可以得到中国科学院软件研究所开具的实习证明，同时也有实习奖金。我们希望这种方式的实习工作能够得到学校的认可，能够抵学生在校期间的实习学分，让学生有更多的动力参与到开源社区，了解开源社区。期望通过开源，通过社区让学生有机会对自己所学的专业、课程等东西更加深入，能够热爱自己的专业，成为未来软件发展的中坚力量。</p>
<blockquote>
<p>我们一直致力在高校和开源社区间建立起一个有效的链接，通过开源的精神和形式，真正的把产业、教学和科研连接在一起，形成一个正向的循环，而不是把高校作为企业研发人力的补充。开源是精神文化，也是形式和平台，让我们一起利用好当前的这个机遇，共同获得成功。</p>
</blockquote>
<hr>
<p>在 openEuler 开源社区的运营实践中，我们针对普通开发者的运营工作更多的是依靠商业牵引，所以在华为的工作体系中有商业拓展体系对开源社区项目的拓展进行支持，这是大多数公司对于开源项目发展给予不了的支持，所以 openEuler 的商业拓展结果强于高校开发者生态的发展速度。这对高校的开发者也是一种牵引，希望商业上的成功能够吸引高校的老师和学生投入到这个领域，但是这种变化的速度是比较缓慢的。如果我们希望社区真正意义上的成功，需要给开源社区一个充分的成长空间。之前我有运营过国内的 Docker 中文社区，而后被 Kubernetes 社区超越。从它诞生到极盛花了一年半的时间，这是至今为止开源社区成长速度最快的。当然这里有 Docker 技术和产品本身的特性，而且当处在 IaaS 发展尽头如何向 PaaS 转身，加上 Solomon 、Brandon 等一众人物的加持，才有了容器技术发展的今天，也才有而后 Kubernetes 出现一统江湖，当然 CNCF、OCI 等一众基金会的成立也是为了削弱 Docker 的发展。从这里我们看到，一个开源项目的成功也离不开“天时”、“地利” 和 “人和” 三点。我从腾讯回到华为参与 openEuler 开源项目的运营也正是看到 openEuler 在当时是具有这三点的。</p>
<p>何为 <strong>“天时”</strong> ？这几年正直中美贸易战愈演愈烈之时，华为遭受各种方向的制裁，从国家政策到公司内外都看到我们在基础软件领域的薄弱，无法支撑自主的发展。这时不管是政府的政策还是公司内的战略都会向基础软件倾斜，才会有华为持续的大规模投资支撑 openEuler 操作系统发展。没有这些战略资源的支持，openEuler 是不可能发展到今天的程度。这些也是商业运营能够快速发展的根本原因。加入 openEuler 社区的 300 多家企业也是看到了这种大势，加入了也不会损失什么，这个车不可能不搭。</p>
<p>何为 <strong>“地利</strong>” ？这 2 年 openEuler 并没有走国际社区的运作路线看，因为疫情出国是有些麻烦的。所以我们把优势的人力和优势的资源都集中在国内，利用华为已有的商业渠道网络、校园合作网络去发展社区的商业合作伙伴，发展高校师生参与到社区。虽然对比 CentOS 、Debian 和 Ubuntu 在时间上 openEuler 已经落后很多，但是在国内这个有限的地利范围内，我们集中优势努力的去弥补时间上差距。在有限的地利空间内，在战略资源优势集中的情况下，我们可以创造局部的优势，成为成功者。</p>
<p>何为 <strong>“人和”</strong> ？在运作 openEuler 操作系统的最初，华为就坚持一个商业策略，华为自己不做 openEuler 的商业发行版，这是和国内其它操作系统商业发行版厂商合作的基础。因为华为一直坚守这个承诺，所以在 openEuler 社区才建立起最核心的一圈伙伴和开发者。这也是社区发展的基石，大家以 openEuler 作为上游开源发行版，建立起一个可以循环的商业场景。没有这些人坚守在社区，也没有机会扩大发展。</p>
<p>如果我们坚持运营一个开源社区，顺应 <strong>“天时、地利、人和”</strong> 之势才有获得成功的可能，另一个方面要给开源社区一个时间，万物发展都有它顺应的规律。之前我谈到 Docker 社区的发展需要了一年半时间，openEuler 从各方面来讲绝对没有当时 Docker 优势，所以经过两年我们也不能讲自己是成功的开源社区，所以也是希望在座的各位能真心的支持 openEuler 开源社区，希望媒体给社区、给开源一些空间，避免过度的宣传造成 “捧杀” 。</p>
<hr>
<p>说到 <strong>“开源”</strong> 和 <strong>“社区”</strong> ，现在我经常被问到如何建立一个社区，如何运作（运营）一个社区。尤其是一些原先和开源搭不上边的机构和组织。尤其是现在搞个社区经常是一个机构牵头，然后说成立某某社区，然后挂靠在一些机构和学会，然后就利用各种行政手段让一些头部的公司来加入。前面这个阶段操作的如火纯青，因为以前搞各种协会套路都很熟悉了。而后就会问一个问题，我怎么运营这个社区，怎么搞开源，面对这种机构的问题我其实内心是很无语的。</p>
<p>首先当我强调社区属于所有参与的人，不属于某个人、某个机构或者建立的人时，我们遇到第一个麻烦的问题。那么开源社区属于谁呢，当然如果从法律角度来说，还有签署 CLA 的各种问题。但是核心的来讲，社区是属于所有参与者的。这个问题其实不光是外部交流起来困难，在华为内部也是如此。我们要清楚的意识到参与开源社区的所有人都是平等的，虽然有些人在社区里面有些 Title ，但是都不代表他们具有权力，这些 Title 代表了他们服务社区的义务。最近有个 Rust 基金会 Moderation Team 辞职的消息，可以看到国外成熟社区的运作模式，任何一个 Team 都是有自己的职责和工作，Team 之间是相互制衡的。那么一个主管机构建立了一个社区，然后听到说这个社区应该是属于所有人的，大家可以想象交流时的场景了。</p>
<p>其次开源社区是一定要有开源项目的，没有开源项目大家就没有交流的标的物了。大家都没有共同感兴趣的话题，这社区还聚体在一起做什么呢？当然也可以像我们可以搞一个云原生喝酒社区😂。那么有多少开源项目能够建立起一个社区呢？我随手写几行代码开源就能搞一个社区么，这明显是不可能的。这里涉及到如何设计一个开源项目，这是个技术问题，也是产品问题。之前我有一些演讲谈到开源项目本身当作是一个产品来设计，今天可能没有更多的机会去讲细节，希望下次能有机会来交流。</p>
<p>最终，我们要讲为什么要运营一个开源社区。从华为的角度运作 openEuler 操作系统是有明确的商业诉求，就是为了华为的鲲鹏服务器建设一个具有完整生态的操作系统，保证鲲鹏服务器的使用是有操作系统的支持。但是很多政府机构，行业机构建立开源社区是为什么？我其实并不是很能理解。但是我最担心的是，将来你要建设一个社区的时候，还需与去一些政府部门的批准或者在政府部门的领导下。像现在开发一个游戏现在还需与申请版号，你将来搞个社区可能还需要找什么部门去备案，只有市场化的充分竞争才能激起更多的创新。</p>
<hr>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[openEuler，未来可期 - 2021 年北京工业大学大师讲坛演讲稿]]></title>
            <link>https://maquanyi.com/articles/openeuler-in-the-feature</link>
            <guid>https://maquanyi.com/articles/openeuler-in-the-feature</guid>
            <pubDate>Thu, 03 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>大家好，我是马全一，目前在华为技术有限公司工作，任开源社区运营总监，主要负责 openEuler 操作系统的开源社区运营，同时也负责 openGauss 、openLooKeng 两个开源项目的运营指导。2000 年参加工作后一直从事技术开发和技术管理等工作；2013 年开始在国内运营 Docker 社区，推广容器、Kubernetes 等云原生技术；2015 年加入华为负责 CNCF 等海外基金会的一些工作，维护华为和谷歌、红帽等云原生领域核心企业的开发者关系，同时也参与了华为云容器相关产品的设计和开发；2017 年底离开华为加入腾讯，负责腾讯云容器产品的设计，领导了腾讯的开源项目 TKEStack ，这个项目也捐赠到开放原子开源基金会；2020 年初回到华为负责 openEuler 操作系统开源社区相关的运营工作。因为工作的原因，这两年也致力于同一些高校操作系统课程的老师合作进行计算机教学改革的探索。</p>
<p>参与 openEuler 让我有机会重回学校，这个机会对我来说是非常宝贵的。我在大学期间学习的是师范类生物教育专业，对计算机相关学科的教学体系并不了解，经过一段时间的摸索后让我对操作系统课程的教学情况有了一些认知，但是也带来了一些疑惑。今天不是来给大家讲操作系统的高深知识，这部分我相信学校的任课教授一定会比我讲的更好更精彩，我今天来是把当前业界操作系统发展的趋势，一些当前操作系统设计背后的理念，以及我对这个行业、包括基础软件未来发展的个人判断带给大家。希望给今天参与这个讲座的学生带来操作系统这些基础软件未来发展方向的一些参考意见，盼望你们在将来择业或者选择学术方向时有一些参考。</p>
<hr>
<p>最近我在读《高效写作》这本书，其中有段话是这样说的</p>
<blockquote>
<p>在学生时代，我们就学会了如何掩盖自己的无知。我们知道如何用长篇累牍来填补自己不知道的东西，也会在游移不定处故作坚定。所以，迷失感确实表明自己力有不逮(dài)，不确定能否言之有物。</p>
</blockquote>
<p>当我这两年和很多高校的操作系统老师一起探讨教学改革时，发现不止是我自己，很多高校的教授和学者也迷失在这快速变革的时代中，无从坚持操作系统课程的初心。 当前大学教授、助教都是有很大的科研压力、论文压力、项目压力和生活压力。生活在这种高成本的社会，需要获取丰厚的收入支撑家庭，还要兼顾工作和科研，每天都沉浸焦虑之中。在这个过程中，每个人都会自然的选择更容易出成果的科研技术方向投入，这也是无可厚非的。这就造成了最近十年大部分科研方向都转向了 AI 领域 ，甚至一些非计算机相关专业的科研方向也都在向 AI 靠拢。各种相关顶级会议和期刊上，来自中国的论文和研究也越来越多。这一改变这也就牵引了我们的研究生、本科生在学习的过程中偏向于这个方向。</p>
<p>不可否认，最近几年应届生高薪的 Offer 也都出在了 AI 这个领域。但是随着 AI 发展，落地场景局限性的问题逐渐显露出来，AI 方向 Offer 数量已经急剧减少，争夺国内几家公司的 AI Offer 就像千军万马争过独木桥一样。我之前在腾讯有参与过实习生、应届生的招聘工作，其中我所在组一名北京交通大学的实习生转正后，我有咨询他具体情况。据他所讲，他所在寝室 4 个人，只有他一个人是搞 Kubernetes 的这样基础软件并且拿到了腾讯的 Offer ，其它 3 个学 AI 室友的都没有拿到 Offer ，最终选择去考公务员或者去银行之类的。当然现在考公务员、从政也是一股潮流，也不能说他们的选择不好。只是市场的调节有时候是滞后的，大家在选择方向时一定要谨慎，也听说教育部也有发文在纠正这个科研和教学方向的发展。</p>
<hr>
<p>那我们返回来讲操作系统是不是有 “钱途”，操作系统的发展是什么样子的，我们到底应该怎么选择呢。首先我们看操作系统是干什么用的，我的观点是操作系统是一个“中间件”，是计算机硬件和应用之间沟通的桥梁，当然操作系统也做了内存管理、进程管理等复杂的事情，但这些功能的核心诉求是让应用更好的使用硬件。</p>
<p>譬如说操作系统的内存管理这些年的演进，就是让应用程序能够使用更大的内存，更高效的使用内存。Linux 系统的交换分区，就是在内存比较昂贵、比较小的年代，划分一部分硬盘空间作为在系统内存不够时使用的，但是近年来按照 Linux 时就不再设置交换分区了，主要是因为现在的服务器内存很大，基本不太出现内存不够用的状况了。在 openEuler 最新的版本发布了一个内存分级扩展的功能，为什么要这样做这样的功能呢？之前配置服务器的时候按照 1 个 CPU 核配 2 G 内存、后来发展到 4G、8G，16G 甚至更大，为的是数据库、虚拟机、大数据、人工智能、深度学习这些场景中需要巨大算力的同时，满足内存的支持。譬如 AI 场景我们就经常需要把大量的数据读入内存或者 GPU 的显存进行计算。所以内存容量成为了制约业务和算力的问题。那么内存分级扩展通过机器本身的高速内存和低速内存介质混合在一起形成多级内存，譬如 RDMA 这种远端的内存就属于低速内存。通过内存自动调度让热数据在高速内存区中运行，让冷数据交换到低速内存区，从而增加内存容量，保证核心业务高效平稳运行。这种方式适用于内存使用量大，且使用相对不频繁的应用进程上，如 MySQL 、Nginx 等。</p>
<p>前面讲我们配置机器的内存是根据计算机的核数来的，要为什么要这样配置呢，CPU 和内存的之间的关系对计算机的性能有什么影响呢？我们知道 CPU 都是有多级缓存的，缓存的速度要比内存快，如果一个计算任务需要的内存数据能够绑定在一个核上计算时，它使用的数据都在固定的内存和缓存上，节省了在核之间调度计算任务，数据在不同的缓存和内存区域进行交换的时间，从而提升了整个的计算效率。那么这里逻辑是什么呢，就是用空间换时间，在一些优化策略里面是时间换空间策略，可以让任务跑的慢一点没关系，但是要保证任务能够完成，前面我们说的设置内存交换空间就是这样的策略。所以我们从操作系统领域到其它的软件的性能调优，其实也都大多是利用这个原则（“时间换空间或空间换时间”）。所以我们在工作的时候经常说，如果脱离开场景，谈性能优化都是刷流氓。硬件规格是固定的，它的计算能力是固定的，不会因为任何调优而凭空产生更多的算力。华为很多针对鲲鹏服务器的在内核层面的调优，在根本上也是基于这个原则，利用了 ARM 上核数多的优势，再加上内存绑定这类技巧，实现在单机的性能上超越 x86 架构的服务器。在我看来，这是由于 x86 的制造工业和发展方向这几年没有什么突破，牙膏厂这几年有些没落的趋势。这部分的内容在 openEuler 操作系统这本书都有，我们最近也在整理相关的内容和实验，把这部分开源在 openEuler 社区。</p>
<p>我们再讲讲 openEuler 做点其它内存方面的优化，在 gcc 和 jdk 方面都做了内存对齐优化。在我们的系统中，CPU 从一个时钟周期读取的内存单元 4 bytes 是连续的，所以编译器在分配数据的时候如果能把数据放到 4 bytes 内，或者连续的 4 bytes 倍数的内存中，CPU 读取的效率也就提高了。</p>
<p>我们看到了这些操作系统以及周边工作的优化都是为了在提升硬件效率，所以我会把操作系统看做中间件，这个中间件就是将硬件经过优化提供给应用程序。</p>
<p>我这里举例的 openEuler 是华为内部使用了近 10 年的操作系统，在 2019 年 12 月 31 日正式宣布开源。openEuler 的包格式选择的是 rpm ，这个是 RedHat 、SUSE 等操作系统普遍选择的包格式，但是打包的规范，openEuler 有自己做了重新的定义，规范上不同于 Fedora ，是独立体系的包格式。当然 openEuler 的内核和其它 Linux 发行版一样是使用的 Linux Kernel ，所有的软件包也是来源于 Linux 上游社区，所有的内核版本选择、软件包版本选择、发布周期等等都有自己独立的规范和规定。按照 Linux 操作系统的约定俗称，openEuler 属于一个 Linux 发行版，和 Fedora、OpenSUSE、Debian、Ubuntu 一样的发型版。如果基于 CentOS 来做的发型版，使用了 CentOS 的内核版本，使用了 CentOS 的包定义，即使是自己重新编译，也不能称之为 Linux 发行版，我们一般称呼这样的发行版位为 CentOS 的衍生版。</p>
<p>当然目前国内的大型企业和云计算厂商使用的是 CentOS 或者自己制作的衍生版本，这个现象产生是有原因的，并不是说 CentOS 就一定有多好。CentOS 是根据 RedHat 企业版的 Linux 内核版本以及包版本编译的社区版本，我们可以把它看作是 RedHat 企业版的克隆。在国内 IT 发展的早期，大家并不是所有的服务器操作系统都购买 RedHat 企业版，只是少部分购买，大部分用 CentOS 这种克隆版本。当出现 CVE 按照漏洞或者内核升级的时候，利用少量购买的 License ，就可以获取到 RedHat 的更新和技术支持，而这些正好是可以用在 CentOS 的克隆版本上的，这样就慢慢形成了国内普遍使用 CentOS 作为服务器的版本。现在随着国内越来越重视版权，这样的行为基本不可见了，绝大部分厂商都购买各种各样的正版操作系统。</p>
<hr>
<p>Linux 操作系统从出现到现在有 30 年的时间，未来它的发展是怎么样的呢，有什么新的技术会影响它的发展，我们如何选择它的方向学习呢？</p>
<p>首先是近几年出现了新的编程语言 Rust ，正在被开发者接受，而且 Linux 内核也是第一次接受 C 语言之外的开发语言，这个对 Linux 内核的发展产生了重大的影响。Rust 相比其它编程语言最大改进是从语言的机制实现了内存安全。自从 2004 年开始，微软安全相应中心(Microsoft Security Response Centre, MSRC) 对每个报告的安全漏洞进行分类。MSRC 的 Matter Miller 在  BlueHat IL 2019 年的会议中公布 70% 的漏洞都和内存安全 (Memory Safety) 相关。虽然 C++ 使用智能指针能避免部分内存安全的问题，但是 C++ 依旧没有完全避免内存安全和数据竞争的问题。依靠开发者做 “正确的事情” 依旧是无法避免内存安全问题。Google 在其漏洞奖励计划 (Vulnerability Rewards Program，VRP) 的统计数据中，内存带来的漏洞占统计数据的 59% ，是占比第一的，同样的 Apple 在其安全报告中也是列举了大量内存相关的漏洞，这也是 Rust 语言产生的重要原因之一。可以看到 Rust 语言引入到 Linux 内核开发中，必将吸引大量的开发者参与，Linux 内核开发生态也将迎来井喷发展。</p>
<p>我们知道 Linux Kernel 是宏内核，随着功能的不断叠加，当前已经成为非常庞大的体系。宏内核在保证效率的同时，也带来了一些开发和安全上的问题。安全上随着功能增加，暴露出来的攻击面也就越大，系统也就越脆弱。之前我在一些公司工作的时候，都是根据服务器的硬件和要在服务器上运行的程序来定制内核，内核通常不大，基本在 4M 左右。当然这些年很少有企业来这样做了，也少有运维工程师有能力根据业务情况定制内核了。在开发层面，由于 Linux Kernel 的庞大体系致使调试非常困难，这个状况也使得社区发布了一些项目来帮助内核开发着，这里面堪称内核调试瑞士军刀的项目就是 ebpf 。ebpf 是从 bpf 也就是 Berkely Packet Filter 发展演进而来的，e 的意思是 Extended ，原先的 bpf 则成为 Classic Berkely Packet Filter 。3.18 版本以后的内核都带有 ePBF 解释器和 JIT 编译器，使得内核能够在运行过程中执行字节码的指令，能够在内核运行过程中输出内核的一些状态。我们可以简单点的理解，它是内核运行过程中的一个虚拟机，你可以在虚拟机中执行一些指令。利用 epbf 可以实现在网卡驱动和内核协议栈之间插入了eBPF扩展的网包过滤、转发功能，这个也就是我们讲的 XDP 功能，这部分中 openEuler 21.09 版本中也是支持的。</p>
<p>从这几年云计算的发展，引入当前操作系统功能模块实现的一个潮流，就是所谓的计算下沉。简单点解释就是之前在内核中做的一部分工作，都在计算机不同的部件中直接完成，而不经过内核，由他们分担一部分内核功能，从而提升效率和速度，我们经常听到到 DPDK 就是这种潮流的代表。为了加速网络传输的速度，讲对网络数据的解包封包和转发，直接在网卡中进行，网卡承担了很多内核做的功能。随着 RISC-V 的逐渐兴起，设计各种各样的计算卡已经成为一种潮流，在未来的服务器中，我们一定会看到多种多样架构的各种硬件参杂在一起。我之前在腾讯内部推广一个理念就是多维异构，意思是软件和硬件都是异构的。软件不管是容器、KVM、rust-vmm 这种底层技术带来的上层 Kubernetes 编排能力增强，会把计算业务、大数据、AI 等混合调度在一个集群中；底层 RISC-V 、ARM64 和 X86 混合在一起形成服务器集群，发挥各自的长处服务于业务。openEuler 虽然生于华为，但是推出的时候就支持 X86 和 ARM64 两种架构，当前我们正在和中国科学院软件研究所的专家们一起向 RISC-V 移植，目标就是支持各种厂商的芯片，支持各种架构，为上层业务提供性能、稳定性和安全的保障。</p>
<hr>
<p>中国科学院软件研究所一直是 openEuler 社区中重要成员之一，openEuler 社区在去年和今年都举办了暑期高校学生的竞赛活动 暑期 2020 和暑期 2021 ，在这两年的活动中有近千名学生参与到开源社区的开发中，在这里不仅仅是 openEuler 社区，更有 100 多个其它开源社区参与进来。在这些活动中，参与的学生有机会和各个公司顶尖的工程师一起工作，学习到新的技能，了解开源社区和参与社区。当然竞赛活动是有选拔的，在众多的报名者中只有少数的学生有机会参与到这个活动中。考虑到这个情况，openEuler 社区也会中近期和中科院软件所发起新的开源社区活动就是 openEuler 开源社区实习。每个学生都可以报名参与 openEuler 开源社区的实习，经过学生资格的认证后，可以在 openEuler 社区选择相关的任务，不同难度和工作量的任务会有不同的积分，在一定时间内当学生完成的一定积分的任务后，由中国科学院软件研究所为学生开具实习证明，同时有一定金额的实习奖金。我们也正在和一些学校进行积极的沟通，目前已经有一些学校认可这个活动的实习证明，可以用来申请实习学分。这个活动的门槛就会低很多，任何学生都可以报名参与，优秀的学生我相信在明年的 暑期 2022 活动中，更容易中选参与更高级别的竞赛活动。实习活动应该在本月发布，希望大家关注 openEuler 公众号、头条号，获取活动报名的最新信息。</p>
<hr>
<p>openEuler 开源社区从始至终都是一个开放的社区，目前有很多国内外的操作系统厂商、硬件厂商、中间件厂商和独立的开发者参与。社区有理事会、技术委员会、各个技术方向 SIG 组等完善的社区组织架构。就在这个月，我们也正式加入了国内唯一的开源基金会 - 开放原子开源基金会，openEuler 的治理和管理都将在基金会和政府的指导下更加规范和开放。目前除中国科学院软件研究所以外还有大连理工大学、兰州大学等大学也参与到社区中，利用 openEuler 开源社区这个平台，为高校计算机教育打造一个学习和科研的平台。</p>
<blockquote>
<p>希望大家能更多的关注操作系统、数据库这些基础软件，希望能够有更多的高校师生参与到开源社区，希望能为大家将来的择业和科研提供更多的帮助。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[开源有道，度量有尺 - 开源需要如何评价呢？]]></title>
            <link>https://maquanyi.com/articles/oss-compass-publish-conference</link>
            <guid>https://maquanyi.com/articles/oss-compass-publish-conference</guid>
            <pubDate>Mon, 27 Feb 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Foss-compass.74094933.jpeg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Foss-compass.74094933.jpeg&amp;w=3840&amp;q=75">
<p><a href="https://oss-compass.org">OSS Compass</a> 项目从去年伊始我就有参与（蹭饭），几个重要地讨论（饭局）我都正好赶上。参与这个项目没有给予太多的帮助，只是经常从意义、价值和对生态影响几个方面给红薯、琨少和晔晖提一些灵魂（无解）的问题。在二月二 "龙抬头" 这个日子把因为疫情一拖再拖的发布会举办，明显是红薯不需要理发，忽略了大多数人需要排队去剪头的需求。我也只能顶着乱糟糟有点白的头发，在圆桌环节和琨少、杨攀等几位调侃。幸亏是有所克制，会场直播才没有出现我经常遇到的 “炸号” 情况。</p>
<img loading="lazy" width="3199" height="2133" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Foss-compass-maquanyi.d5d3a1db.jpg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Foss-compass-maquanyi.d5d3a1db.jpg&amp;w=3840&amp;q=75">
<blockquote>
<p>这里讲一下没有在会场上说的话，自己的网站发一些文字就不存在 “炸号” 的可能，反正也没有指望它能坚持多久不被 “墙” 。</p>
</blockquote>
<h3>“开源项目度量” 是把 “双刃剑”</h3>
<img loading="lazy" width="4032" height="2268" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Foss-compass-1.804a87c6.jpeg&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Foss-compass-1.804a87c6.jpeg&amp;w=3840&amp;q=75">
<p>开源项目为什么要度量？ 度量是一把标尺，使用这个标尺的是一些公司或者 “相关” 机构为了管理 “开源项目” 而采用的先进 “科学” 手段。 有了度量体系就有度量结果，可以把管理 “开源项目” 进行量化、可视化，多种工作可以转换为 KPI 落地执行。年初大家最头疼的工作不就是要定 KPI 么，有人已经把如何考核以及考核平台都做好递到 BOSS 手上了。对于开源运营的人来说，不管是 “社区经理” 还是 “运营总监” ，为了 KPI 就可以 “八仙过海，各显神通” 了，根据指标分析权重，就能找到最短路径提升度量结果。 所以要不要引入 AI 来分析呢，要不还是让 ChatGPT 来评价我们的 “开源项目” 吧。</p>
<p>我觉得以前的问题是 “仅仅依靠简单的指标来评价项目的成果和贡献者的价值, 可能会误导开源社区的发展方向, 阻碍真正有价值的创新”。 现在复杂的指标体系就能真正地推动创新么？ 我一直认为 “开源” 的兴起是自由经济市场充分竞争的结果，那现在谁应该负责给 “开源” 一个符合发展的宽松环境让其充分发展呢？ 举国体制和市场体制的矛盾之下，到底是什么样的 “开源” 成为中国的主流？ 会不会我们又一次靠一个管理机构，毁掉了一个刚刚发展起来的 "职业" 联赛，把多年球迷的梦想扼杀在世界杯预选赛？</p>
<p>当然短期来看，这样的度量体系和平台也终于挤了开源项目的一部分水分。 还没有等游泳馆关门换水，就有一个水泵在抽游泳池的水。 很多浅水区的 “开源项目” 可能就马上漏出了泳裤。 有些敢穿 “皇帝新衣” 的 “开源项目” 会暴露在众多开发者面前。 Compass 相关不光在抽水，还是那个说出实话的 “小朋友” 。这平台的未来让人担心，希望不要被 “招安”，要 "中立" 、"有尊严" 的活下去。</p>
<h3>开源项目只是开源社区的表相，社区中的开发者是如何被度量？</h3>
<p>通常我们看到一个开源项目，会首先关注开源项目的代码、文档、Issue 等方面，当然这些也是构成评价一个开源项目的核心数据。 但是开源项目的是由开发者、用户和其他的贡献者共同努力的结果，可以说是许多人的劳动结晶。 一个开源项目的核心其实背后的开发者，他们才是真正为开源项目创造价值的人。 但是如何去正确地评价他们呢？</p>
<p>举个例子，Linus Torvalds 不知何故看到了 "你" 的一个开源项目，当然以他的性格肯定是大骂你写的一堆狗屎代码，并且在邮件列表和 Issue 上公开出来。 那么势必引起众多围观的吃瓜群众分析你的 "屎山" 。那么请问 Linus 的举动对这个开源项目的价值如何？ 如果不是 Linus 而是一个普通的开发者，那这个 Issue 的价值又该如何评价？</p>
<p>现在的模型对于开发者本身的评价还是没有什么科学的办法，这也许是专注此方向的学者们可以进行突破的地方。</p>
<blockquote>
<p>所以如何去评价一个开源项目呢，片面的 ”Star“ 肯定是不可取，全面的数据分析是否能够覆盖一个开源项目的所有方向，这应该是 Compass 项目去真正思考的问题。 对于我们参与开源项目的开发者或是其他的贡献者来说，我们要关注的是什么呢？ 想起来我之前经常和别人聊天说的一个观点，”如果我选择一个开源项目，最影响我的因素是 - 我认识还是不认识这个项目的核心开发者“ 。这个观点是不是有点过分了，但是我想说的是，如果有想反驳的请去 <a href="https://maquanyi.notion.site/40bd811a586343628c1d0411e1b0d1b2">https://maquanyi.notion.site/40bd811a586343628c1d0411e1b0d1b2</a> 评论，我保证虚心接受坚决不改。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[吃货游南京赋诗一首 - 无题]]></title>
            <link>https://maquanyi.com/articles/poem-nanjing-no-title</link>
            <guid>https://maquanyi.com/articles/poem-nanjing-no-title</guid>
            <pubDate>Sat, 29 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>聽風雨玄武湖畔，飲精釀萬尺高樓。
望行雲紫金山下，食湯包市井巷中。</p>
<blockquote>
<p>到南京出差，正逢台风登陆福建，影响到南京也风雨大作。 住绿地洲际酒店，能远望紫金山，低看玄武湖，是一种忙中偷闲的享受。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[和 @dustise 合作出品，不解释不接受反驳，请勿对号入座，转载请随意]]></title>
            <link>https://maquanyi.com/articles/poem-with-dustise</link>
            <guid>https://maquanyi.com/articles/poem-with-dustise</guid>
            <pubDate>Fri, 04 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>专家台上吼，大神遍地走; 架构堆积木，框架多如狗; 测试惜如金，文档没发瞅; 开源集成商，上市路上走。</h2>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[反思产品设计中一次失败的经历]]></title>
            <link>https://maquanyi.com/articles/reflecting-on-a-failed-experience-in-design</link>
            <guid>https://maquanyi.com/articles/reflecting-on-a-failed-experience-in-design</guid>
            <pubDate>Tue, 23 Apr 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="Issue" loading="lazy" width="940" height="450" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F1.7a68499a.webp&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2F1.7a68499a.webp&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F1.7a68499a.webp&amp;w=1920&amp;q=75">
<p>默默开发的一个 <strong>DAG (Directed Acyclic Graphs)</strong> 引擎项目 <a href="https://github.com/open-rust-initiative/dagrs">Dagrs</a> ，在 <a href="https://crates.io/crates/dagrs">crates.io</a> 上发布了两个版本。从来没有在任何渠道宣传过，竟然有开发者在自己的项目中使用。最近突然收到一个 <a href="https://github.com/open-rust-initiative/dagrs/issues/60">Issue</a> 提到 DAG 任务中返回值的设计，思考之中让我对之前所做的工作进行了反思，这个项目可以说是产品设计失败的典型。</p>
<blockquote>
<p>Hey! First of all, thanks for this crate and all your work for it :) In my use case it's crucial to allow individual task errors. This crate already provides the Output enum for task outputs but only uses the variant Output::Err(String) for logging. I would really like the ability to propagate the contained string value as well as the task identifier all the way to the start function. This way, a user knows failed tasks and can work with that information in his code.
What do you think of this idea? I'd be happy to work on this issue.</p>
</blockquote>
<p>当我准备这个博客的时候，发现邮箱收到这个开发者都邮件，可能是我没有回复他的 Issue 使得他尝试从邮件来联系我。 这些年的开发者相关工作，让我觉得老外是容易打交道的，只要真诚和对方沟通就好。</p>
<hr>
<p>设计这个项目源自工作早期项目，经历了 Delphi 开发财务为核心的各种管理系统、SAP ERP FI/CO 实施和开发，以及极少人知道的 BMC Remedy 开发 ITSM 项目，我理解从程序角度看完成特定功能是调度函数完成任务，函数之间存在上下文的切换，函数内存在各种条件判断。如果再简化看这些函数之间的关系，它们形成了一个有向无环图，最终根据程序的输入条件生成输出的结果。所以一直有个简单的想法，通过 DAG 引擎调度程序内的函数，完成程序的基本功能。</p>
<p>当我尝试在 Rust 中完成这个引擎的时候，我犯了一个产品设计中最严重的错误，就是尝试引入很多特性，忽略了系统功能的边界，造成整体可用性下降和代码架构混乱。在基本功能实现后，加入了一个 YAML 脚本定义函数之间的关系以及上下文传递，在 YAML 中定义可以执行外部命令。最后甚至想把这个 YAML 改为一个可以自定的结构并且自动生成脚本解析引擎。所作的这些是想把这个定义行为的逻辑通过这样的功能传递到系统外，让使用程序的用户（也可能是另外的一个开发者）可以进行定义，这明显是超出了之前的初衷。</p>
<blockquote>
<p>Less is More</p>
</blockquote>
<p>23 年参加 EuroRust 2023 的时候听一个演讲提到 Flow-base Programming 的概念，研究后发现这就是我想做的事情。最近在 Discord 上看到一个 Rust 编程语言的实现 <a href="https://github.com/zflow-dev/zflow">zflow</a>，对比之下更是看到我在设计上的问题，尝试把 Flow 的控制管理交给边界之外的用户。</p>
<p>其实初衷我既不想做一个 Airflow、 Remedy 或是低代码平台，我只是想把烦躁重复的业务性开发简化。我面向的用户是开发者，而不是使用程序的用户。所以错误的根本是没有坚持初衷，盲目的扩大程序边界和面向的用户，贪大忘本造成了现在的局面。</p>
<h3>我的计划</h3>
<p>准备先删掉那些多余的功能，让整个程序只提供最基本的能力，发布一个最简版本。然后发邮件给使用这个项目的开发者，和他们讨论需求和遇到的问题，最后联合一起重构。</p>
<hr>
<img alt="Issue" loading="lazy" width="898" height="340" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2.3f9150be.webp&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2.3f9150be.webp&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2.3f9150be.webp&amp;w=1920&amp;q=75">
<p>以上是最近项目每天 Clone 数，估计是 GitHub Action 在执行时产生的 Clone 。很多开发者都在想说如何去推广自己的开源项目，其实首先要思考项目提供的功能是什么，再去想如何解决酒香也怕巷子深的问题。</p>
<hr>
<h3>朋友圈及公众号留言精选</h3>
<blockquote>
<p>项目的初衷最初都是美好的，关于这个项目的抽象理念我也遇到过几个相似场景：一个是借助线程池和dag构建一个单机版的调度器，可以按照拓扑顺序并行拉起用户的udf，这个还是有需求的，可以认为是简易版的调度器。还有一个是用workflow的思路描述sql执行逻辑，informatica和MuleSoft好像做了类似的事情，有点像sql的低代码版本，但实际上开发者并不买单，所以用workflow表达高级程序语言的func call的通用性和易用性门槛就更高了。话说用DAG表达函数调用逻辑时，遇到递归可就有环了</p>
</blockquote>
<blockquote>
<p>我觉得能反思的案例太多了， 这才可怕... 总得有几个不用反思的成功，要不然亏大了</p>
</blockquote>
<blockquote>
<p>有用户就不算失败</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Ruff - 使用 Rust 开发的 Python Linter 工具]]></title>
            <link>https://maquanyi.com/articles/ruff-python-lint-by-rust-astral</link>
            <guid>https://maquanyi.com/articles/ruff-python-lint-by-rust-astral</guid>
            <pubDate>Wed, 03 May 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Linters 是用于分析程序源代码的一种工具，用来检测如语法错误、样式等。它们对于维护项目中的代码质量和可读性非常重要，同时还可以在开发周期的早期捕捉到 bug。在 Rust 中我们通常使用 <a href="https://github.com/rust-lang/rust-clippy">Clippy</a> ，它已经内置了超过 600 多个规则，和编译器一起成为我学习 Rust 编程语言的一种方式。在 Python 中有 <a href="https://pylint.readthedocs.io/">Pylint</a>、<a href="https://flake8.pycqa.org/">Flake8</a>、<a href="https://github.com/PyCQA/pyflakes">Pyflakes</a> 和 <a href="https://pycodestyle.pycqa.org/">pycodestyle（pep8</a>）多个使用 Python 实现的 lint 工具，每一种工具都有自己的检查规则。Ruff 是 2022 年 8 月发布的，使用 Rust 实现的一个新的 Python Linter 工具。在 macOS 环境下使用 CPython 的代码仓库进行测试，比  <a href="https://pylint.readthedocs.io/">Pylint</a>、<a href="https://flake8.pycqa.org/">Flake8</a>、<a href="https://github.com/PyCQA/pyflakes">Pyflakes</a> 和 <a href="https://pycodestyle.pycqa.org/">pycodestyle（pep8</a>）都快出了很多倍。</p>
<blockquote>
<p>"Lint" 的词源可以追溯到古英语中的 "lynnet" 或 "linet"，意为亚麻布的残余物。后来这个词被用来指棉花或纺织品生产过程中的残余物。在计算机编程中，"lint" 一词被用来指一种静态代码分析工具，可以扫描代码中的潜在问题和错误。这种使用方法的来源可以追溯到1960年代，在当时的贝尔实验室，一名计算机科学家发明了一个名为 "lint" 的工具，用于帮助发现代码中的错误。这个工具的名称被选中是因为它的创始人发现代码中的问题有时像棉绒一样微小而难以看到，而 "lint" 就是处理棉花时产生的残余物。- <strong>From ChatGPT</strong></p>
</blockquote>
<p>Ruff 实现了一个缓存机制，在每次检查的时候只对上次检查修改过的代码进行扫描。如果使用 Flake8 实现类似的缓存机制，必须使用 <a href="https://pypi.org/project/flake8-cached/">flake8-cache</a> 。Ruff 实现了以上 lint 工具的部分规则，加上自己定义的规则已经接近 500 条。Ruff 执行速度快于用 Python 实现的同类型工具的主要原因是，使用了 <a href="https://rustpython.github.io/">RustPython</a> 的 AST（Abstract Syntax Tree），针对每个 Python 文件都生成 AST 提升遍历的速度。</p>
<blockquote>
<p>抽象语法树（Abstract Syntax Tree，简称 AST）是一种用于表示源代码结构的树形数据结构。在编译器和解释器的工作过程中，AST 是源代码语法分析（parsing）阶段的核心产物。其目的是以更简洁、更高层次的方式表示源代码的逻辑结构，以便于进一步的编译、优化和执行。- <strong>From ChatGPT</strong></p>
</blockquote>
<p>Ruff 可以集成到现在已有的编辑器中，或 Language Server Protocol 中，在 GitHub Action 中使用将极大的提高 Action 的执行速度。</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">name</span><span class="token punctuation">:</span> Ruff
<span class="token key atrule">on</span><span class="token punctuation">:</span> <span class="token punctuation">[</span> push<span class="token punctuation">,</span> pull_request <span class="token punctuation">]</span>
<span class="token key atrule">jobs</span><span class="token punctuation">:</span>
  <span class="token key atrule">ruff</span><span class="token punctuation">:</span>
    <span class="token key atrule">runs-on</span><span class="token punctuation">:</span> ubuntu<span class="token punctuation">-</span>latest
    <span class="token key atrule">steps</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/checkout@v3
      <span class="token punctuation">-</span> <span class="token key atrule">uses</span><span class="token punctuation">:</span> chartboost/ruff<span class="token punctuation">-</span>action@v1
				<span class="token key atrule">with</span><span class="token punctuation">:</span>
				    <span class="token key atrule">src</span><span class="token punctuation">:</span> <span class="token string">"./src"</span>
				    <span class="token key atrule">version</span><span class="token punctuation">:</span> 0.0.259
				    <span class="token key atrule">args</span><span class="token punctuation">:</span> <span class="token punctuation">-</span><span class="token punctuation">-</span>select B
</code></pre>
<p>更多信息访问 <strong><a href="https://beta.ruff.rs/docs/">官方文档</a></strong> 或阅读原文 <strong><a href="https://lwn.net/Articles/930487/">Ruff: a fast Python linter</a> 。</strong></p>
<p>4 月中 Ruff 的作者 Charlie Marsh 宣布成立了一家叫 <a href="https://astral.sh/">Astral</a> 的 Startup 公司，针对 Python 生态开发更多的工具，使用 Rust 带来更多效率的提升。</p>
<blockquote>
<p>从 Deno、Artichoke 到 RustPython ，包括各种使用 Rust 实现的编程语言，可以看到 Rust 有成为编程语言底层实现的一个选择，Rust 也逐渐成为整个底层基础软件领域的重要选择；海外的商业生存环境是比国内友好太多，一个开发者工具就可以成为一个创业公司的起点，在当前国内无处不卷死的大环境下，这样的公司是不可能产生，也扼杀了很多创新的机会。当很多人还讨论商业和开源的关系时，别人已经从开源走向商业了。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Rust for Linux 学习笔记系列 - 第一章 开发环境搭建]]></title>
            <link>https://maquanyi.com/articles/rust-for-linux-series-one-set-environment</link>
            <guid>https://maquanyi.com/articles/rust-for-linux-series-one-set-environment</guid>
            <pubDate>Thu, 17 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<blockquote>
<p>首先声明一下，本系列笔记学习是记录学习过程中的内容，是从互联网收集到的各种资料进行学习整理的笔记，所以我会把学习资源列到最后。当然肯定会有一些错误，如果有错误的地方欢迎指正，请 TG 联系 <strong>genedna</strong> 。</p>
</blockquote>
<p><a href="https://github.com/Rust-for-Linux">Rust for Linux</a> [1] 的环境搭建，是我看完 <strong>Linux Foundation</strong> 的教学视频后跟着一步步做后总结整理的，大家有时间的话推荐还是看一下 <a href="(https://www.youtube.com/watch?v=tPs1uRqOnlk)">原视频</a> [2]，这样会更加清晰。视频中 <strong>Wedson Almeida Filho</strong> 是使用了一个 <strong>Ubuntu 21.04</strong> 的虚拟机来进行演示，我在本地测试的环境是安装在小米笔记本上的 <strong>Ubuntu 22.10</strong> 环境。虽然两个系统环境有点细节上的差异，但是我参照执行后并没有任何问题。</p>
<h3>1. 安装依赖的库和软件</h3>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> <span class="token function">git</span> flex bison clang llvm lld libelf-dev qemu-kvm
</code></pre>
<h3>2. 安装 Rust 环境</h3>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">curl</span> --proto <span class="token string">'=https'</span> --tlsv1.2 -sSf https://sh.rustup.rs <span class="token operator">|</span> <span class="token function">sh</span>
</code></pre>
<p>当前我安装的是 <strong>Rust</strong> 的 <strong>1.65.0</strong> 版本，可以通过 <code>rustc --version</code> 查看。根据 <strong>Wedson</strong> 在视频里面的解释，目前 <a href="https://github.com/Rust-for-Linux">Rust for Linux</a> 使用的最小版本是 <strong>1.62.0</strong>，但是目前没有测试更高版本是否有问题. 所以我这里安装的版本是比较新的, 所以需要安装一个较低的版本。</p>
<p>在安装较低版本前，先 <code>Clone</code> <a href="https://github.com/Rust-for-Linux">Rust for Linux</a> 的代码到本地，为此我创建了一个 GitHub 的目录。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">mkdir</span> GitHub
$ <span class="token builtin class-name">cd</span> GitHub
$ <span class="token function">git</span> clone --depth<span class="token operator">=</span><span class="token number">1</span> https://github.com/Rust-for-Linux/linux.git
正克隆到 <span class="token string">'linux'</span><span class="token punctuation">..</span>.
remote: Enumerating objects: <span class="token number">83487</span>, done.
remote: Counting objects: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">83487</span>/83487<span class="token punctuation">)</span>, done.
remote: Compressing objects: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">75322</span>/75322<span class="token punctuation">)</span>, done.
remote: Total <span class="token number">83487</span> <span class="token punctuation">(</span>delta <span class="token number">7737</span><span class="token punctuation">)</span>, reused <span class="token number">72487</span> <span class="token punctuation">(</span>delta <span class="token number">7313</span><span class="token punctuation">)</span>, pack-reused <span class="token number">0</span>
接收对象中: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">83487</span>/83487<span class="token punctuation">)</span>, <span class="token number">231.81</span> MiB <span class="token operator">|</span> <span class="token number">6.68</span> MiB/s, 完成.
处理 delta 中: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">7737</span>/7737<span class="token punctuation">)</span>, 完成.
正在更新文件: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">78804</span>/78804<span class="token punctuation">)</span>, 完成.
</code></pre>
<blockquote>
<p>这时候我们可以看到当前分之是 <strong>rust</strong> 分支，这个分支是 <strong>Rust for Linux</strong> 的主分支，我们可以通过 <code>git branch</code> 查看当前分支，注意不要切换到其它分之。</p>
</blockquote>
<p>然后进入到 <code>linux</code> 目录下，安装最小版本的 <strong>Rust</strong> 编译环境。</p>
<pre class="language-bash"><code class="language-bash">$ rustup override <span class="token builtin class-name">set</span> <span class="token variable"><span class="token variable">$(</span>scripts/min-tool-version.sh rustc<span class="token variable">)</span></span>
info: syncing channel updates <span class="token keyword">for</span> <span class="token string">'1.62.0-x86_64-unknown-linux-gnu'</span>
info: latest update on <span class="token number">2022</span>-06-30, rust version <span class="token number">1.62</span>.0 <span class="token punctuation">(</span>a8314ef7d <span class="token number">2022</span>-06-27<span class="token punctuation">)</span>
info: downloading component <span class="token string">'cargo'</span>
  <span class="token number">6.6</span> MiB /   <span class="token number">6.6</span> MiB <span class="token punctuation">(</span><span class="token number">100</span> %<span class="token punctuation">)</span>   <span class="token number">4.6</span> MiB/s <span class="token keyword">in</span>  1s ETA:  0s
info: downloading component <span class="token string">'clippy'</span>
info: downloading component <span class="token string">'rust-docs'</span>
 <span class="token number">18.3</span> MiB /  <span class="token number">18.3</span> MiB <span class="token punctuation">(</span><span class="token number">100</span> %<span class="token punctuation">)</span>   <span class="token number">8.6</span> MiB/s <span class="token keyword">in</span>  2s ETA:  0s
info: downloading component <span class="token string">'rust-std'</span>
 <span class="token number">26.0</span> MiB /  <span class="token number">26.0</span> MiB <span class="token punctuation">(</span><span class="token number">100</span> %<span class="token punctuation">)</span>   <span class="token number">6.2</span> MiB/s <span class="token keyword">in</span>  4s ETA:  0s
info: downloading component <span class="token string">'rustc'</span>
 <span class="token number">54.1</span> MiB /  <span class="token number">54.1</span> MiB <span class="token punctuation">(</span><span class="token number">100</span> %<span class="token punctuation">)</span>   <span class="token number">8.2</span> MiB/s <span class="token keyword">in</span>  7s ETA:  0s
info: downloading component <span class="token string">'rustfmt'</span>
info: installing component <span class="token string">'cargo'</span>
info: installing component <span class="token string">'clippy'</span>
info: installing component <span class="token string">'rust-docs'</span>
 <span class="token number">18.3</span> MiB /  <span class="token number">18.3</span> MiB <span class="token punctuation">(</span><span class="token number">100</span> %<span class="token punctuation">)</span>   <span class="token number">9.5</span> MiB/s <span class="token keyword">in</span>  1s ETA:  0s
info: installing component <span class="token string">'rust-std'</span>
 <span class="token number">26.0</span> MiB /  <span class="token number">26.0</span> MiB <span class="token punctuation">(</span><span class="token number">100</span> %<span class="token punctuation">)</span>  <span class="token number">12.7</span> MiB/s <span class="token keyword">in</span>  1s ETA:  0s
info: installing component <span class="token string">'rustc'</span>
 <span class="token number">54.1</span> MiB /  <span class="token number">54.1</span> MiB <span class="token punctuation">(</span><span class="token number">100</span> %<span class="token punctuation">)</span>  <span class="token number">15.4</span> MiB/s <span class="token keyword">in</span>  3s ETA:  0s
info: installing component <span class="token string">'rustfmt'</span>
info: override toolchain <span class="token keyword">for</span> <span class="token string">'/home/eli/GitHub/linux'</span> <span class="token builtin class-name">set</span> to <span class="token string">'1.62.0-x86_64-unknown-linux-gnu'</span>

  <span class="token number">1.62</span>.0-x86_64-unknown-linux-gnu installed - rustc <span class="token number">1.62</span>.0 <span class="token punctuation">(</span>a8314ef7d <span class="token number">2022</span>-06-27<span class="token punctuation">)</span>
$ rustc --version
rustc <span class="token number">1.62</span>.0 <span class="token punctuation">(</span>a8314ef7d <span class="token number">2022</span>-06-27<span class="token punctuation">)</span>
</code></pre>
<p>使用 <code>make LLVM=1 rustavailable</code> 检查 <strong>Rust</strong> 环境的其它组件是否完整。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> rustavailable
***
*** Rust bindings generator <span class="token string">'bindgen'</span> could not be found.
***
make<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>: *** <span class="token punctuation">[</span>rustavailable<span class="token punctuation">]</span> Error <span class="token number">1</span>
make: *** <span class="token punctuation">[</span>__sub-make<span class="token punctuation">]</span> Error <span class="token number">2</span>
</code></pre>
<p><code>bindgen</code> 是自动生成 Rust FFI 与 C/C++ 库绑定的工具，内核 C 的代码绑定是使用它生成的，所以需要安装指定版本的 <code>bindgen</code>。 在之前安装系统环境的时候我们就安装了 <code>LLVM</code> 和 <code>clang</code>，但是所以这里不再提示相关的错误。</p>
<pre class="language-bash"><code class="language-bash">$ cargo <span class="token function">install</span> --locked --version <span class="token variable"><span class="token variable">$(</span>scripts/min-tool-version.sh bindgen<span class="token variable">)</span></span> bindgen
  Downloaded bindgen v0.56.0
  Downloaded <span class="token number">1</span> crate <span class="token punctuation">(</span><span class="token number">198.3</span> KB<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">3</span>.85s
    Updating crates.io index
  Installing bindgen v0.56.0
  Downloaded memchr v2.3.4
  Downloaded proc-macro2 v1.0.24
  Downloaded clap v2.33.3
  Downloaded glob v0.3.0
  Downloaded regex v1.4.2
  Downloaded rustc-hash v1.1.0
  Downloaded thread_local v1.0.1
  Downloaded <span class="token function">which</span> v3.1.1
  Downloaded version_check v0.9.2
  Downloaded termcolor v1.1.0
  Downloaded libc v0.2.80
  Downloaded aho-corasick v0.7.15
  Downloaded regex-syntax v0.6.21
  Downloaded shlex v0.1.1
  Downloaded peeking_take_while v0.1.2
  Downloaded log v0.4.11
  Downloaded bitflags v1.2.1
  Downloaded humantime v2.0.1
  Downloaded lazycell v1.3.0
  Downloaded cexpr v0.4.0
  Downloaded ansi_term v0.11.0
  Downloaded nom v5.1.2
  Downloaded libloading v0.6.5
  Downloaded env_logger v0.8.1
  Downloaded clang-sys v1.0.3
  Downloaded <span class="token number">25</span> crates <span class="token punctuation">(</span><span class="token number">1.9</span> MB<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">3</span>.65s
   Compiling memchr v2.3.4
   Compiling libc v0.2.80
   Compiling glob v0.3.0
   Compiling version_check v0.9.2
   Compiling lazy_static v1.4.0
   Compiling log v0.4.11
   Compiling bitflags v1.2.1
   Compiling proc-macro2 v1.0.24
   Compiling cfg-if v1.0.0
   Compiling cfg-if v0.1.10
   Compiling unicode-xid v0.2.1
   Compiling unicode-width v0.1.8
   Compiling regex-syntax v0.6.21
   Compiling humantime v2.0.1
   Compiling ansi_term v0.11.0
   Compiling strsim v0.8.0
   Compiling bindgen v0.56.0
   Compiling vec_map v0.8.2
   Compiling termcolor v1.1.0
   Compiling shlex v0.1.1
   Compiling rustc-hash v1.1.0
   Compiling lazycell v1.3.0
   Compiling peeking_take_while v0.1.2
   Compiling thread_local v1.0.1
   Compiling libloading v0.6.5
   Compiling textwrap v0.11.0
   Compiling nom v5.1.2
   Compiling clang-sys v1.0.3
   Compiling aho-corasick v0.7.15
   Compiling quote v1.0.7
   Compiling atty v0.2.14
   Compiling <span class="token function">which</span> v3.1.1
   Compiling clap v2.33.3
   Compiling regex v1.4.2
   Compiling env_logger v0.8.1
   Compiling cexpr v0.4.0
    Finished release <span class="token punctuation">[</span>optimized<span class="token punctuation">]</span> target<span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> 3m 30s
  Installing /home/eli/.cargo/bin/bindgen
   Installed package <span class="token variable"><span class="token variable">`</span>bindgen v0.56.0<span class="token variable">`</span></span> <span class="token punctuation">(</span>executable <span class="token variable"><span class="token variable">`</span>bindgen<span class="token variable">`</span></span><span class="token punctuation">)</span>
</code></pre>
<p>再次使用 <code>make LLVM=1 rustavailable</code> 检查 <strong>Rust</strong> 环境的其它组件是否完整，会发现需要安装 <strong>Rust</strong> 的源代码来交叉编译 <code>core</code> 和 <code>alloc</code> 库。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> rustavailable
***
*** Source code <span class="token keyword">for</span> the <span class="token string">'core'</span> standard library could not be found
*** at <span class="token string">'/Users/eli/.rustup/toolchains/1.62.0-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/lib.rs'</span><span class="token builtin class-name">.</span>
***
make<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>: *** <span class="token punctuation">[</span>rustavailable<span class="token punctuation">]</span> Error <span class="token number">1</span>
make: *** <span class="token punctuation">[</span>__sub-make<span class="token punctuation">]</span> Error <span class="token number">2</span>
$ rustup component <span class="token function">add</span> rust-src
info: downloading component <span class="token string">'rust-src'</span>
info: installing component <span class="token string">'rust-src'</span>
</code></pre>
<p>最后检查 <strong>Rust</strong> 环境已经准备好了。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> rustavailable
Rust is available<span class="token operator">!</span>
</code></pre>
<p>如果提交 <strong>PR</strong> 给 <a href="https://github.com/Rust-for-Linux">Rust for Linux</a> ，需要对代码进行格式化和 <a href="https://github.com/rust-lang/rust-clippy">Clippy</a> 检查，所以需要安装 <strong>rustfmt</strong> 和 <strong>clippy</strong> 组件。</p>
<pre class="language-bash"><code class="language-bash">$ rustup component <span class="token function">add</span> rustfmt
$ rustup component <span class="token function">add</span> clippy
</code></pre>
<h3>3. 安装 Busybox 环境</h3>
<p>光有 <strong>Rust</strong> 环境还不够，还需要安装 <a href="https://busybox.net">Busybox</a> 环境。 <strong>Busybox</strong> 是一个非常小的 <strong>Linux</strong> 发行版，它包含了 <strong>Linux</strong> 系统中的大部分命令，比如 <code>ls</code>、<code>cat</code>、<code>grep</code> 等等。 调试环境的主要是利用 <code>QEMU</code> 把 <a href="https://github.com/Rust-for-Linux">Rust for Linux</a> 编译出的内核，加上 <strong>Busybox</strong> 的文件系统运行在虚拟机中，这样就可以在虚拟机中进行调试了。</p>
<p>首先是下载 <strong>Busybox</strong> 的源代码。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">git</span> clone --depth<span class="token operator">=</span><span class="token number">1</span> https://github.com/mirror/busybox.git
正克隆到 <span class="token string">'busybox'</span><span class="token punctuation">..</span>.
remote: Enumerating objects: <span class="token number">2301</span>, done.
remote: Counting objects: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">2301</span>/2301<span class="token punctuation">)</span>, done.
remote: Compressing objects: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">1904</span>/1904<span class="token punctuation">)</span>, done.
remote: Total <span class="token number">2301</span> <span class="token punctuation">(</span>delta <span class="token number">119</span><span class="token punctuation">)</span>, reused <span class="token number">1443</span> <span class="token punctuation">(</span>delta <span class="token number">60</span><span class="token punctuation">)</span>, pack-reused <span class="token number">0</span>
接收对象中: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">2301</span>/2301<span class="token punctuation">)</span>, <span class="token number">3.33</span> MiB <span class="token operator">|</span> <span class="token number">1.44</span> MiB/s, 完成.
处理 delta 中: <span class="token number">100</span>% <span class="token punctuation">(</span><span class="token number">119</span>/119<span class="token punctuation">)</span>, 完成.
</code></pre>
<p>进入到 <strong>Busybox</strong> 的源代码目录，使用默认的配置初始化 <code>.config</code>文件。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> busybox
$ <span class="token function">make</span> defconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/split-include
  HOSTCC  scripts/basic/docproc
  GEN     include/applets.h
  GEN     include/usage.h
  GEN     modutils/Kbuild
  GEN     modutils/Config.in
  GEN     e2fsprogs/Kbuild
  GEN     e2fsprogs/Config.in
  GEN     shell/Kbuild
  GEN     shell/Config.in
  GEN     console-tools/Kbuild
  GEN     console-tools/Config.in
  GEN     editors/Kbuild
  GEN     editors/Config.in
  GEN     runit/Kbuild
  GEN     runit/Config.in
  GEN     archival/Kbuild
  GEN     archival/Config.in
  GEN     archival/libarchive/Kbuild
  GEN     scripts/Kbuild
  GEN     klibc-utils/Kbuild
  GEN     klibc-utils/Config.in
  GEN     debianutils/Kbuild
  GEN     debianutils/Config.in
  GEN     libbb/Kbuild
  GEN     libbb/Config.in
  GEN     findutils/Kbuild
  GEN     findutils/Config.in
  GEN     coreutils/Kbuild
  GEN     coreutils/Config.in
  GEN     coreutils/libcoreutils/Kbuild
  GEN     miscutils/Kbuild
  GEN     miscutils/Config.in
  GEN     init/Kbuild
  GEN     init/Config.in
  GEN     procps/Kbuild
  GEN     procps/Config.in
  GEN     mailutils/Kbuild
  GEN     mailutils/Config.in
  GEN     sysklogd/Kbuild
  GEN     sysklogd/Config.in
  GEN     networking/Kbuild
  GEN     networking/Config.in
  GEN     networking/libiproute/Kbuild
  GEN     networking/udhcp/Kbuild
  GEN     networking/udhcp/Config.in
  GEN     libpwdgrp/Kbuild
  GEN     loginutils/Kbuild
  GEN     loginutils/Config.in
  GEN     selinux/Kbuild
  GEN     selinux/Config.in
  GEN     util-linux/Kbuild
  GEN     util-linux/Config.in
  GEN     util-linux/volume_id/Kbuild
  GEN     util-linux/volume_id/Config.in
  GEN     applets/Kbuild
  GEN     printutils/Kbuild
  GEN     printutils/Config.in
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/kxgettext.o
  HOSTCC  scripts/kconfig/mconf.o
  SHIPPED scripts/kconfig/zconf.tab.c
  SHIPPED scripts/kconfig/lex.zconf.c
  SHIPPED scripts/kconfig/zconf.hash.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf

<span class="token comment"># 此处略去 1000 行</span>

scripts/kconfig/conf -d Config.in
</code></pre>
<blockquote>
<p>对于太多的输出，笔记中做了适当的截取，会保留关键的输出。</p>
</blockquote>
<p>通过 <code>menuconfig</code> 命令调整参数实现编译静态的二进制文件。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> menuconfig
</code></pre>
<p><img src="/articles/rust-for-linux-series/rust-for-linux-1-2.png" alt="busy-box-menuconfig-1"></p>
<p><img src="/articles/rust-for-linux-series/rust-for-linux-1-3.png" alt="busy-box-menuconfig-2"></p>
<p>执行 <code>make install</code> 命令后会整个发行版会安装在 <code>_install</code> 目录中。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token function">install</span>
  ./_install//bin/arch -<span class="token operator">&gt;</span> busybox
  ./_install//bin/ash -<span class="token operator">&gt;</span> busybox
  ./_install//bin/base32 -<span class="token operator">&gt;</span> busybox
  ./_install//bin/base64 -<span class="token operator">&gt;</span> busybox
  ./_install//bin/cat -<span class="token operator">&gt;</span> busybox
  ./_install//bin/chattr -<span class="token operator">&gt;</span> busybox
  ./_install//bin/chgrp -<span class="token operator">&gt;</span> busybox

<span class="token comment"># 此处略去 1000 行</span>

--------------------------------------------------
You will probably need to <span class="token function">make</span> your busybox binary
setuid root to ensure all configured applets will
work properly.
--------------------------------------------------

$ <span class="token function">ls</span> _install/
bin  linuxrc  sbin  usr
</code></pre>
<p>这样获得了一个基本可用的发行版文件系统。</p>
<h3>4. 编译内核，加入 Rust 支持</h3>
<p>因为要引导 <strong>Busybox</strong> 的文件系统，所以先使用 <code>qemu-busybox-min.config</code> 设置生成基本的内核配置文件。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> linux
$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> allnoconfig qemu-busybox-min.config
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  HOSTCC  scripts/kconfig/lexer.lex.o
  HOSTCC  scripts/kconfig/menu.o
  HOSTCC  scripts/kconfig/parser.tab.o
  HOSTCC  scripts/kconfig/preprocess.o
  HOSTCC  scripts/kconfig/symbol.o
  HOSTCC  scripts/kconfig/util.o
  HOSTLD  scripts/kconfig/conf
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>
Using .config as base
Merging ./kernel/configs/qemu-busybox-min.config
Value of CONFIG_SMP is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_SMP is not set</span>
New value: <span class="token assign-left variable">CONFIG_SMP</span><span class="token operator">=</span>y

Value of CONFIG_PRINTK_TIME is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_PRINTK_TIME is not set</span>
New value: <span class="token assign-left variable">CONFIG_PRINTK_TIME</span><span class="token operator">=</span>y

Value of CONFIG_PCI is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_PCI is not set</span>
New value: <span class="token assign-left variable">CONFIG_PCI</span><span class="token operator">=</span>y

Value of CONFIG_BLK_DEV_INITRD is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_BLK_DEV_INITRD is not set</span>
New value: <span class="token assign-left variable">CONFIG_BLK_DEV_INITRD</span><span class="token operator">=</span>y

Value of CONFIG_BINFMT_ELF is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_BINFMT_ELF is not set</span>
New value: <span class="token assign-left variable">CONFIG_BINFMT_ELF</span><span class="token operator">=</span>y

Value of CONFIG_DEVTMPFS is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_DEVTMPFS is not set</span>
New value: <span class="token assign-left variable">CONFIG_DEVTMPFS</span><span class="token operator">=</span>y

Value of CONFIG_NET is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_NET is not set</span>
New value: <span class="token assign-left variable">CONFIG_NET</span><span class="token operator">=</span>y

Value of CONFIG_DEBUG_KERNEL is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_DEBUG_KERNEL is not set</span>
New value: <span class="token assign-left variable">CONFIG_DEBUG_KERNEL</span><span class="token operator">=</span>y

Value of CONFIG_INPUT_EVDEV is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_INPUT_EVDEV is not set</span>
New value: <span class="token assign-left variable">CONFIG_INPUT_EVDEV</span><span class="token operator">=</span>y

Value of CONFIG_INPUT_KEYBOARD is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_INPUT_KEYBOARD is not set</span>
New value: <span class="token assign-left variable">CONFIG_INPUT_KEYBOARD</span><span class="token operator">=</span>y

Merging ./arch/x86/configs/qemu-busybox-min.config
Value of CONFIG_64BIT is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_64BIT is not set</span>
New value: <span class="token assign-left variable">CONFIG_64BIT</span><span class="token operator">=</span>y

Value of CONFIG_ACPI is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_ACPI is not set</span>
New value: <span class="token assign-left variable">CONFIG_ACPI</span><span class="token operator">=</span>y

Value of CONFIG_SERIAL_8250 is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_SERIAL_8250 is not set</span>
New value: <span class="token assign-left variable">CONFIG_SERIAL_8250</span><span class="token operator">=</span>y

Value of CONFIG_HYPERVISOR_GUEST is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_HYPERVISOR_GUEST is not set</span>
New value: <span class="token assign-left variable">CONFIG_HYPERVISOR_GUEST</span><span class="token operator">=</span>y

Value of CONFIG_CMDLINE_BOOL is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_CMDLINE_BOOL is not set</span>
New value: <span class="token assign-left variable">CONFIG_CMDLINE_BOOL</span><span class="token operator">=</span>y

<span class="token comment">#</span>
<span class="token comment"># merged configuration written to .config (needs make)</span>
<span class="token comment">#</span>
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>

</code></pre>
<p>然后加入 <code>Rust</code> 的配置。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> allnoconfig qemu-busybox-min.config rust.config
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>
Using .config as base
Merging ./kernel/configs/qemu-busybox-min.config
Value of CONFIG_SMP is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_SMP is not set</span>
New value: <span class="token assign-left variable">CONFIG_SMP</span><span class="token operator">=</span>y

Value of CONFIG_PRINTK_TIME is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_PRINTK_TIME is not set</span>
New value: <span class="token assign-left variable">CONFIG_PRINTK_TIME</span><span class="token operator">=</span>y

Value of CONFIG_PCI is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_PCI is not set</span>
New value: <span class="token assign-left variable">CONFIG_PCI</span><span class="token operator">=</span>y

Value of CONFIG_BLK_DEV_INITRD is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_BLK_DEV_INITRD is not set</span>
New value: <span class="token assign-left variable">CONFIG_BLK_DEV_INITRD</span><span class="token operator">=</span>y

Value of CONFIG_BINFMT_ELF is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_BINFMT_ELF is not set</span>
New value: <span class="token assign-left variable">CONFIG_BINFMT_ELF</span><span class="token operator">=</span>y

Value of CONFIG_DEVTMPFS is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_DEVTMPFS is not set</span>
New value: <span class="token assign-left variable">CONFIG_DEVTMPFS</span><span class="token operator">=</span>y

Value of CONFIG_NET is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_NET is not set</span>
New value: <span class="token assign-left variable">CONFIG_NET</span><span class="token operator">=</span>y

Value of CONFIG_DEBUG_KERNEL is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_DEBUG_KERNEL is not set</span>
New value: <span class="token assign-left variable">CONFIG_DEBUG_KERNEL</span><span class="token operator">=</span>y

Value of CONFIG_INPUT_EVDEV is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_INPUT_EVDEV is not set</span>
New value: <span class="token assign-left variable">CONFIG_INPUT_EVDEV</span><span class="token operator">=</span>y

Value of CONFIG_INPUT_KEYBOARD is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_INPUT_KEYBOARD is not set</span>
New value: <span class="token assign-left variable">CONFIG_INPUT_KEYBOARD</span><span class="token operator">=</span>y

Merging ./arch/x86/configs/qemu-busybox-min.config
Value of CONFIG_64BIT is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_64BIT is not set</span>
New value: <span class="token assign-left variable">CONFIG_64BIT</span><span class="token operator">=</span>y

Value of CONFIG_ACPI is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_ACPI is not set</span>
New value: <span class="token assign-left variable">CONFIG_ACPI</span><span class="token operator">=</span>y

Value of CONFIG_SERIAL_8250 is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_SERIAL_8250 is not set</span>
New value: <span class="token assign-left variable">CONFIG_SERIAL_8250</span><span class="token operator">=</span>y

Value of CONFIG_HYPERVISOR_GUEST is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_HYPERVISOR_GUEST is not set</span>
New value: <span class="token assign-left variable">CONFIG_HYPERVISOR_GUEST</span><span class="token operator">=</span>y

Value of CONFIG_CMDLINE_BOOL is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_CMDLINE_BOOL is not set</span>
New value: <span class="token assign-left variable">CONFIG_CMDLINE_BOOL</span><span class="token operator">=</span>y

<span class="token comment">#</span>
<span class="token comment"># merged configuration written to .config (needs make)</span>
<span class="token comment">#</span>
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>
Using .config as base
Merging ./kernel/configs/rust.config
<span class="token comment">#</span>
<span class="token comment"># merged configuration written to .config (needs make)</span>
<span class="token comment">#</span>
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>
$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> allnoconfig qemu-busybox-min.config rust.config
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>
Using .config as base
Merging ./kernel/configs/qemu-busybox-min.config
Value of CONFIG_SMP is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_SMP is not set</span>
New value: <span class="token assign-left variable">CONFIG_SMP</span><span class="token operator">=</span>y

Value of CONFIG_PRINTK_TIME is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_PRINTK_TIME is not set</span>
New value: <span class="token assign-left variable">CONFIG_PRINTK_TIME</span><span class="token operator">=</span>y

Value of CONFIG_PCI is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_PCI is not set</span>
New value: <span class="token assign-left variable">CONFIG_PCI</span><span class="token operator">=</span>y

Value of CONFIG_BLK_DEV_INITRD is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_BLK_DEV_INITRD is not set</span>
New value: <span class="token assign-left variable">CONFIG_BLK_DEV_INITRD</span><span class="token operator">=</span>y

Value of CONFIG_BINFMT_ELF is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_BINFMT_ELF is not set</span>
New value: <span class="token assign-left variable">CONFIG_BINFMT_ELF</span><span class="token operator">=</span>y

Value of CONFIG_DEVTMPFS is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_DEVTMPFS is not set</span>
New value: <span class="token assign-left variable">CONFIG_DEVTMPFS</span><span class="token operator">=</span>y

Value of CONFIG_NET is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_NET is not set</span>
New value: <span class="token assign-left variable">CONFIG_NET</span><span class="token operator">=</span>y

Value of CONFIG_DEBUG_KERNEL is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_DEBUG_KERNEL is not set</span>
New value: <span class="token assign-left variable">CONFIG_DEBUG_KERNEL</span><span class="token operator">=</span>y

Value of CONFIG_INPUT_EVDEV is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_INPUT_EVDEV is not set</span>
New value: <span class="token assign-left variable">CONFIG_INPUT_EVDEV</span><span class="token operator">=</span>y

Value of CONFIG_INPUT_KEYBOARD is redefined by fragment ./kernel/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_INPUT_KEYBOARD is not set</span>
New value: <span class="token assign-left variable">CONFIG_INPUT_KEYBOARD</span><span class="token operator">=</span>y

Merging ./arch/x86/configs/qemu-busybox-min.config
Value of CONFIG_64BIT is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_64BIT is not set</span>
New value: <span class="token assign-left variable">CONFIG_64BIT</span><span class="token operator">=</span>y

Value of CONFIG_ACPI is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_ACPI is not set</span>
New value: <span class="token assign-left variable">CONFIG_ACPI</span><span class="token operator">=</span>y

Value of CONFIG_SERIAL_8250 is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_SERIAL_8250 is not set</span>
New value: <span class="token assign-left variable">CONFIG_SERIAL_8250</span><span class="token operator">=</span>y

Value of CONFIG_HYPERVISOR_GUEST is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_HYPERVISOR_GUEST is not set</span>
New value: <span class="token assign-left variable">CONFIG_HYPERVISOR_GUEST</span><span class="token operator">=</span>y

Value of CONFIG_CMDLINE_BOOL is redefined by fragment ./arch/x86/configs/qemu-busybox-min.config:
Previous value: <span class="token comment"># CONFIG_CMDLINE_BOOL is not set</span>
New value: <span class="token assign-left variable">CONFIG_CMDLINE_BOOL</span><span class="token operator">=</span>y

<span class="token comment">#</span>
<span class="token comment"># merged configuration written to .config (needs make)</span>
<span class="token comment">#</span>
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>
Using .config as base
Merging ./kernel/configs/rust.config
Value of CONFIG_RUST is redefined by fragment ./kernel/configs/rust.config:
Previous value: <span class="token comment"># CONFIG_RUST is not set</span>
New value: <span class="token assign-left variable">CONFIG_RUST</span><span class="token operator">=</span>y

<span class="token comment">#</span>
<span class="token comment"># merged configuration written to .config (needs make)</span>
<span class="token comment">#</span>
<span class="token comment">#</span>
<span class="token comment"># configuration written to .config</span>
<span class="token comment">#</span>
</code></pre>
<p>重点的是我们看到 <code>New value: CONFIG_RUST=y</code>，这就是我们想要的，现在就可以编译成功具有 <strong>Rust</strong> 支持的内核版本了。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> -j8
  SYNC    include/config/auto.conf.cmd
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_32.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_64.h
  SYSHDR  arch/x86/include/generated/uapi/asm/unistd_x32.h
  SYSTBL  arch/x86/include/generated/asm/syscalls_32.h
  SYSHDR  arch/x86/include/generated/asm/unistd_32_ia32.h
  SYSHDR  arch/x86/include/generated/asm/unistd_64_x32.h
  SYSTBL  arch/x86/include/generated/asm/syscalls_64.h
  WRAP    arch/x86/include/generated/uapi/asm/bpf_perf_event.h
  WRAP    arch/x86/include/generated/uapi/asm/errno.h
  WRAP    arch/x86/include/generated/uapi/asm/fcntl.h
  WRAP    arch/x86/include/generated/uapi/asm/ioctls.h
  WRAP    arch/x86/include/generated/uapi/asm/ipcbuf.h
  WRAP    arch/x86/include/generated/uapi/asm/ioctl.h
  WRAP    arch/x86/include/generated/uapi/asm/param.h
  WRAP    arch/x86/include/generated/uapi/asm/poll.h
  WRAP    arch/x86/include/generated/uapi/asm/resource.h
  WRAP    arch/x86/include/generated/uapi/asm/socket.h
  WRAP    arch/x86/include/generated/uapi/asm/sockios.h
  WRAP    arch/x86/include/generated/uapi/asm/termbits.h
  WRAP    arch/x86/include/generated/uapi/asm/termios.h
  WRAP    arch/x86/include/generated/uapi/asm/types.h
  HOSTCC  arch/x86/tools/relocs_32.o
  HOSTCC  arch/x86/tools/relocs_64.o
  HOSTCC  arch/x86/tools/relocs_common.o
  WRAP    arch/x86/include/generated/asm/early_ioremap.h
  WRAP    arch/x86/include/generated/asm/export.h
  WRAP    arch/x86/include/generated/asm/mcs_spinlock.h
  WRAP    arch/x86/include/generated/asm/irq_regs.h
  WRAP    arch/x86/include/generated/asm/kmap_size.h
  WRAP    arch/x86/include/generated/asm/local64.h
  WRAP    arch/x86/include/generated/asm/mmiowb.h
  WRAP    arch/x86/include/generated/asm/module.lds.h
  WRAP    arch/x86/include/generated/asm/rwonce.h
  WRAP    arch/x86/include/generated/asm/unaligned.h
  UPD     include/config/kernel.release
  UPD     include/generated/uapi/linux/version.h
  HOSTCC  scripts/kallsyms
  HOSTCC  scripts/sorttable
  HOSTRUSTC scripts/generate_rust_target
  UPD     include/generated/compile.h
  HOSTRUSTC scripts/rustdoc_test_builder
  UPD     include/generated/utsrelease.h
  HOSTRUSTC scripts/rustdoc_test_gen
  DESCEND objtool
  HOSTCC  /home/eli/GitHub/linux/tools/objtool/fixdep.o
  HOSTLD  /home/eli/GitHub/linux/tools/objtool/fixdep-in.o
  HOSTLD  arch/x86/tools/relocs
  LINK    /home/eli/GitHub/linux/tools/objtool/fixdep
  CC      /home/eli/GitHub/linux/tools/objtool/arch/x86/special.o
  CC      /home/eli/GitHub/linux/tools/objtool/check.o
  CC      /home/eli/GitHub/linux/tools/objtool/weak.o
  MKDIR   /home/eli/GitHub/linux/tools/objtool/arch/x86/lib/
  GEN     /home/eli/GitHub/linux/tools/objtool/arch/x86/lib/inat-tables.c
  CC      /home/eli/GitHub/linux/tools/objtool/exec-cmd.o
  CC      /home/eli/GitHub/linux/tools/objtool/special.o
  CC      /home/eli/GitHub/linux/tools/objtool/arch/x86/decode.o
  CC      /home/eli/GitHub/linux/tools/objtool/builtin-check.o
  CC      /home/eli/GitHub/linux/tools/objtool/help.o
  CC      /home/eli/GitHub/linux/tools/objtool/elf.o
  CC      /home/eli/GitHub/linux/tools/objtool/objtool.o
  CC      /home/eli/GitHub/linux/tools/objtool/orc_gen.o
  CC      /home/eli/GitHub/linux/tools/objtool/orc_dump.o
  CC      /home/eli/GitHub/linux/tools/objtool/libstring.o
  CC      /home/eli/GitHub/linux/tools/objtool/pager.o
  CC      /home/eli/GitHub/linux/tools/objtool/libctype.o
  CC      /home/eli/GitHub/linux/tools/objtool/parse-options.o
  CC      /home/eli/GitHub/linux/tools/objtool/run-command.o
  CC      /home/eli/GitHub/linux/tools/objtool/str_error_r.o
  CC      /home/eli/GitHub/linux/tools/objtool/librbtree.o
  CC      /home/eli/GitHub/linux/tools/objtool/sigchain.o
  CC      /home/eli/GitHub/linux/tools/objtool/subcmd-config.o
  CC      scripts/mod/empty.o
  HOSTCC  scripts/mod/mk_elfconfig
  CC      scripts/mod/devicetable-offsets.s
  LD      /home/eli/GitHub/linux/tools/objtool/arch/x86/objtool-in.o
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  HOSTCC  scripts/mod/sumversion.o
  UPD     scripts/mod/devicetable-offsets.h
  HOSTCC  scripts/mod/file2alias.o
  LD      /home/eli/GitHub/linux/tools/objtool/libsubcmd-in.o
  AR      /home/eli/GitHub/linux/tools/objtool/libsubcmd.a
  HOSTLD  scripts/mod/modpost
  CC      kernel/bounds.s
  CHKSHA1 include/linux/atomic/atomic-arch-fallback.h
  CHKSHA1 include/linux/atomic/atomic-instrumented.h
  CHKSHA1 include/linux/atomic/atomic-long.h
  UPD     include/generated/timeconst.h
  UPD     include/generated/bounds.h
  CC      arch/x86/kernel/asm-offsets.s
  LD      /home/eli/GitHub/linux/tools/objtool/objtool-in.o
  LINK    /home/eli/GitHub/linux/tools/objtool/objtool
  UPD     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  GEN     scripts/gdb/linux/constants.py
  UPD     rust/target.json
  BINDGEN rust/bindings/bindings_generated.rs
  BINDGEN rust/bindings/bindings_helpers_generated.rs
  RUSTC L rust/core.o

<span class="token comment"># 此处略去 1000 行</span>

Kernel: arch/x86/boot/bzImage is ready  <span class="token punctuation">(</span><span class="token comment">#1)</span>
</code></pre>
<blockquote>
<p>每次编译成功后就会有对应的编号生成，这里生成了 <strong>#1</strong> 的编号。</p>
</blockquote>
<h3>5. 编译 Rust 的 Echo Server 例子</h3>
<p>为了验证，使用 <strong>Rust</strong> 的 <strong>Echo Server</strong> 例子。为了能在启动的时候看到输出，我们先修改一下代码。在 <code>samples/rust</code> 目录下，修改 <code>rust_echo_server.rs</code> 文件增加一行打印 <code>pr_info!("Hello from echo server\n");</code>。</p>
<pre class="language-rust"><code class="language-rust"><span class="token keyword">impl</span> <span class="token namespace">kernel<span class="token punctuation">::</span></span><span class="token class-name">Module</span> <span class="token keyword">for</span> <span class="token class-name">RustEchoServer</span> <span class="token punctuation">{</span>
    <span class="token keyword">fn</span> <span class="token function-definition function">init</span><span class="token punctuation">(</span>_name<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'static</span> <span class="token class-name">CStr</span><span class="token punctuation">,</span> _module<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'static</span> <span class="token class-name">ThisModule</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">Self</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
        <span class="token macro property">pr_info!</span><span class="token punctuation">(</span><span class="token string">"Hello from echo server\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">let</span> handle <span class="token operator">=</span> <span class="token class-name">WqExecutor</span><span class="token punctuation">::</span><span class="token function">try_new</span><span class="token punctuation">(</span><span class="token namespace">kernel<span class="token punctuation">::</span>workqueue<span class="token punctuation">::</span></span><span class="token function">system</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
        <span class="token function">start_listener</span><span class="token punctuation">(</span>handle<span class="token punctuation">.</span><span class="token function">executor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
        <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token keyword">Self</span> <span class="token punctuation">{</span>
            _handle<span class="token punctuation">:</span> handle<span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>然后我们把 <code>rust_echo_server</code> 编译成内核模块。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> menuconfig
</code></pre>
<p><img src="/articles/rust-for-linux-series/rust-for-linux-1-4.png" alt="busy-box-menuconfig-2"></p>
<p><img src="/articles/rust-for-linux-series/rust-for-linux-1-5.png" alt="busy-box-menuconfig-2"></p>
<p><img src="/articles/rust-for-linux-series/rust-for-linux-1-6.png" alt="busy-box-menuconfig-2"></p>
<p><img src="/articles/rust-for-linux-series/rust-for-linux-1-7.png" alt="busy-box-menuconfig-2"></p>
<p><img src="/articles/rust-for-linux-series/rust-for-linux-1-8.png" alt="busy-box-menuconfig-2"></p>
<p>再次尝试编译带有 <strong>Rust</strong> 支持和 <strong>Echo Server</strong> 例子的内核。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">make</span> <span class="token assign-left variable">LLVM</span><span class="token operator">=</span><span class="token number">1</span> -j8
  SYNC    include/config/auto.conf.cmd
  DESCEND objtool
  CALL    scripts/checksyscalls.sh
  AR      samples/vfio-mdev/built-in.a
  RUSTC     samples/rust/rust_echo_server.o
  AR      samples/rust/built-in.a
  AR      samples/built-in.a
  AR      built-in.a
  AR      vmlinux.a
  LD      vmlinux.o
  OBJCOPY modules.builtin.modinfo
  GEN     modules.builtin
  MODPOST vmlinux.symvers
  UPD     include/generated/utsversion.h
  CC      init/version-timestamp.o
  LD      .tmp_vmlinux.kallsyms1
ld.lld: warning: <span class="token operator">&lt;</span>internal<span class="token operator">&gt;</span>:<span class="token punctuation">(</span>.eh_frame<span class="token punctuation">)</span> is being placed <span class="token keyword">in</span> <span class="token string">'.eh_frame'</span>
  NM      .tmp_vmlinux.kallsyms1.syms
  KSYMS   .tmp_vmlinux.kallsyms1.S
  AS      .tmp_vmlinux.kallsyms1.S
  LD      .tmp_vmlinux.kallsyms2
ld.lld: warning: <span class="token operator">&lt;</span>internal<span class="token operator">&gt;</span>:<span class="token punctuation">(</span>.eh_frame<span class="token punctuation">)</span> is being placed <span class="token keyword">in</span> <span class="token string">'.eh_frame'</span>
  NM      .tmp_vmlinux.kallsyms2.syms
  KSYMS   .tmp_vmlinux.kallsyms2.S
  AS      .tmp_vmlinux.kallsyms2.S
  LD      vmlinux
ld.lld: warning: <span class="token operator">&lt;</span>internal<span class="token operator">&gt;</span>:<span class="token punctuation">(</span>.eh_frame<span class="token punctuation">)</span> is being placed <span class="token keyword">in</span> <span class="token string">'.eh_frame'</span>
  NM      System.map
  SORTTAB vmlinux
  CC      arch/x86/boot/version.o
  VOFFSET arch/x86/boot/compressed/<span class="token punctuation">..</span>/voffset.h
  OBJCOPY arch/x86/boot/compressed/vmlinux.bin
  GZIP    arch/x86/boot/compressed/vmlinux.bin.gz
  CC      arch/x86/boot/compressed/misc.o
  MKPIGGY arch/x86/boot/compressed/piggy.S
  AS      arch/x86/boot/compressed/piggy.o
  LD      arch/x86/boot/compressed/vmlinux
  ZOFFSET arch/x86/boot/zoffset.h
  OBJCOPY arch/x86/boot/vmlinux.bin
  AS      arch/x86/boot/header.o
  LD      arch/x86/boot/setup.elf
  OBJCOPY arch/x86/boot/setup.bin
  BUILD   arch/x86/boot/bzImage
Kernel: arch/x86/boot/bzImage is ready  <span class="token punctuation">(</span><span class="token comment">#2)</span>
</code></pre>
<blockquote>
<p>编译成功了，这里生成了 <strong>#2</strong> 的编号。</p>
</blockquote>
<h3>6. 准备 Busybox 的环境</h3>
<p>为了能够在内核中运行 <strong>Rust</strong> 的 <strong>Echo Server</strong> 例子，我们需要准备一个 <strong>Busybox</strong> 的环境。 教学视频中对 <strong>Busybox</strong> 做了几次修改，这里为了简单起见，我直接跳到最后的配置版本。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> busybox/__install
$ <span class="token function">mkdir</span> etc
$ <span class="token function">cp</span> <span class="token punctuation">..</span>/examples/inittab /etc/
$ <span class="token function">ls</span>
bin  etc  linuxrc  sbin  usr
$ <span class="token function">ls</span> etc/inittab
etc/inittab
$ <span class="token function">vim</span> etc/inittab
</code></pre>
<p>这里注释掉 <code>inittab</code> 中 <code>tty</code> 相关的代码。</p>
<pre class="language-editorconfig"><code class="language-editorconfig"><span class="token comment"># Start an "askfirst" shell on /dev/tty2-4</span>
<span class="token comment"># tty2::askfirst:-/bin/sh</span>
<span class="token comment"># tty3::askfirst:-/bin/sh</span>
<span class="token comment"># tty4::askfirst:-/bin/sh</span>

<span class="token comment"># /sbin/getty invocations for selected ttys</span>
<span class="token comment"># tty4::respawn:/sbin/getty 38400 tty5</span>
<span class="token comment"># tty5::respawn:/sbin/getty 38400 tty6</span>
</code></pre>
<p>配置启动执行的脚本</p>
<pre><code>$ mkdir etc/init.d
$ vim etc/init.d/rcS
</code></pre>
<pre class="language-editorconfig"><code class="language-editorconfig">mkdir -p /proc
mount -t proc none /proc
ifconfig lo up
udhcpc -i eth0
mkdir -p /dev
mount -t devtmpfs none /dev
mkdir -p /dev/pts
mount -t devpts none /dev/pts

telnetd -l /bin/sh
</code></pre>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">chmod</span> a+x etc/init.d/rcS
</code></pre>
<p>准备 <strong>DHCP</strong> 服务的启动文件。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">mkdir</span> -p usr/share/udhcpc
$ <span class="token function">cp</span> <span class="token punctuation">..</span>/examples/udhcp/simple.script usr/share/udhcpc/default.script
$ <span class="token function">cat</span> usr/share/udhcpc/default.script
<span class="token comment">#!/bin/sh</span>
<span class="token comment"># udhcpc script edited by Tim Riker &lt;Tim@Rikers.org&gt;</span>

<span class="token assign-left variable">RESOLV_CONF</span><span class="token operator">=</span><span class="token string">"/etc/resolv.conf"</span>

<span class="token punctuation">[</span> -n <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">{</span> <span class="token builtin class-name">echo</span> <span class="token string">"Error: should be called from udhcpc"</span><span class="token punctuation">;</span> <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token assign-left variable">NETMASK</span><span class="token operator">=</span><span class="token string">""</span>
<span class="token keyword">if</span> <span class="token builtin class-name">command</span> -v <span class="token function">ip</span> <span class="token operator">&gt;</span>/dev/null<span class="token punctuation">;</span> <span class="token keyword">then</span>
	<span class="token punctuation">[</span> -n <span class="token string">"<span class="token variable">$subnet</span>"</span> <span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token assign-left variable">NETMASK</span><span class="token operator">=</span><span class="token string">"/<span class="token variable">$subnet</span>"</span>
<span class="token keyword">else</span>
	<span class="token punctuation">[</span> -n <span class="token string">"<span class="token variable">$subnet</span>"</span> <span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token assign-left variable">NETMASK</span><span class="token operator">=</span><span class="token string">"netmask <span class="token variable">$subnet</span>"</span>
<span class="token keyword">fi</span>
<span class="token assign-left variable">BROADCAST</span><span class="token operator">=</span><span class="token string">"broadcast +"</span>
<span class="token punctuation">[</span> -n <span class="token string">"<span class="token variable">$broadcast</span>"</span> <span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token assign-left variable">BROADCAST</span><span class="token operator">=</span><span class="token string">"broadcast <span class="token variable">$broadcast</span>"</span>

<span class="token keyword">case</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token keyword">in</span>
	deconfig<span class="token punctuation">)</span>
		<span class="token builtin class-name">echo</span> <span class="token string">"Clearing IP addresses on <span class="token variable">$interface</span>, upping it"</span>
		<span class="token keyword">if</span> <span class="token builtin class-name">command</span> -v <span class="token function">ip</span> <span class="token operator">&gt;</span>/dev/null<span class="token punctuation">;</span> <span class="token keyword">then</span>
			<span class="token function">ip</span> -4 addr flush dev <span class="token variable">$interface</span>
			<span class="token function">ip</span> <span class="token function">link</span> <span class="token builtin class-name">set</span> dev <span class="token variable">$interface</span> up
		<span class="token keyword">else</span>
			<span class="token function">ifconfig</span> <span class="token variable">$interface</span> <span class="token number">0.0</span>.0.0
		<span class="token keyword">fi</span>
		<span class="token punctuation">;</span><span class="token punctuation">;</span>

	renew<span class="token operator">|</span>bound<span class="token punctuation">)</span>
		<span class="token builtin class-name">echo</span> <span class="token string">"Setting IP address <span class="token variable">$ip</span> on <span class="token variable">$interface</span>"</span>
		<span class="token keyword">if</span> <span class="token builtin class-name">command</span> -v <span class="token function">ip</span> <span class="token operator">&gt;</span>/dev/null<span class="token punctuation">;</span> <span class="token keyword">then</span>
			<span class="token function">ip</span> addr <span class="token function">add</span> <span class="token variable">$ip</span><span class="token variable">$NETMASK</span> <span class="token variable">$BROADCAST</span> dev <span class="token variable">$interface</span>
		<span class="token keyword">else</span>
			<span class="token function">ifconfig</span> <span class="token variable">$interface</span> <span class="token variable">$ip</span> <span class="token variable">$NETMASK</span> <span class="token variable">$BROADCAST</span>
		<span class="token keyword">fi</span>

		<span class="token keyword">if</span> <span class="token punctuation">[</span> -n <span class="token string">"<span class="token variable">$router</span>"</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">then</span>
			<span class="token builtin class-name">echo</span> <span class="token string">"Deleting routers"</span>
			<span class="token keyword">while</span> route del default gw <span class="token number">0.0</span>.0.0 dev <span class="token variable">$interface</span> <span class="token punctuation">;</span> <span class="token keyword">do</span>
				<span class="token builtin class-name">:</span>
			<span class="token keyword">done</span>

			<span class="token assign-left variable">metric</span><span class="token operator">=</span><span class="token number">0</span>
			<span class="token keyword">for</span> <span class="token for-or-select variable">i</span> <span class="token keyword">in</span> <span class="token variable">$router</span> <span class="token punctuation">;</span> <span class="token keyword">do</span>
				<span class="token builtin class-name">echo</span> <span class="token string">"Adding router <span class="token variable">$i</span>"</span>
				<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$subnet</span>"</span> <span class="token operator">=</span> <span class="token string">"255.255.255.255"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
	<span class="token comment"># special case for /32 subnets:</span>
	<span class="token comment"># /32 instructs kernel to always use routing for all outgoing packets</span>
	<span class="token comment"># (they can never be sent to local subnet - there is no local subnet for /32).</span>
	<span class="token comment"># Used in datacenters, avoids the need for private ip-addresses between two hops.</span>
					<span class="token function">ip</span> route <span class="token function">add</span> <span class="token variable">$i</span> dev <span class="token variable">$interface</span>
				<span class="token keyword">fi</span>
				route <span class="token function">add</span> default gw <span class="token variable">$i</span> dev <span class="token variable">$interface</span> metric <span class="token variable"><span class="token variable">$((</span>metric<span class="token operator">++</span><span class="token variable">))</span></span>
			<span class="token keyword">done</span>
		<span class="token keyword">fi</span>

		<span class="token comment"># If the file is a symlink somewhere (like /etc/resolv.conf</span>
		<span class="token comment"># pointing to /run/resolv.conf), make sure things work.</span>
		<span class="token keyword">if</span> <span class="token builtin class-name">test</span> -L <span class="token string">"<span class="token variable">$RESOLV_CONF</span>"</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
			<span class="token comment"># If it's a dangling symlink, try to create the target.</span>
			<span class="token builtin class-name">test</span> -e <span class="token string">"<span class="token variable">$RESOLV_CONF</span>"</span> <span class="token operator">||</span> <span class="token function">touch</span> <span class="token string">"<span class="token variable">$RESOLV_CONF</span>"</span>
		<span class="token keyword">fi</span>
		<span class="token assign-left variable">realconf</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>readlink -f <span class="token string">"<span class="token variable">$RESOLV_CONF</span>"</span> <span class="token operator"><span class="token file-descriptor important">2</span>&gt;</span>/dev/null <span class="token operator">||</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$RESOLV_CONF</span>"</span><span class="token variable">)</span></span>
		<span class="token builtin class-name">echo</span> <span class="token string">"Recreating <span class="token variable">$realconf</span>"</span>
		<span class="token assign-left variable">tmpfile</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$realconf</span>-<span class="token variable">$$</span>"</span>
		<span class="token operator">&gt;</span> <span class="token string">"<span class="token variable">$tmpfile</span>"</span>
		<span class="token punctuation">[</span> -n <span class="token string">"<span class="token variable">$domain</span>"</span> <span class="token punctuation">]</span> <span class="token operator">&amp;&amp;</span> <span class="token builtin class-name">echo</span> <span class="token string">"search <span class="token variable">$domain</span>"</span> <span class="token operator">&gt;&gt;</span> <span class="token string">"<span class="token variable">$tmpfile</span>"</span>
		<span class="token keyword">for</span> <span class="token for-or-select variable">i</span> <span class="token keyword">in</span> <span class="token variable">$dns</span> <span class="token punctuation">;</span> <span class="token keyword">do</span>
			<span class="token builtin class-name">echo</span> <span class="token string">" Adding DNS server <span class="token variable">$i</span>"</span>
			<span class="token builtin class-name">echo</span> <span class="token string">"nameserver <span class="token variable">$i</span>"</span> <span class="token operator">&gt;&gt;</span> <span class="token string">"<span class="token variable">$tmpfile</span>"</span>
		<span class="token keyword">done</span>
		<span class="token function">mv</span> <span class="token string">"<span class="token variable">$tmpfile</span>"</span> <span class="token string">"<span class="token variable">$realconf</span>"</span>
		<span class="token punctuation">;</span><span class="token punctuation">;</span>
<span class="token keyword">esac</span>

<span class="token builtin class-name">exit</span> <span class="token number">0</span>
</code></pre>
<p>压缩 <strong>Busybox</strong> 的文件系统为镜像文件。</p>
<pre class="language-bash"><code class="language-bash">
$ <span class="token function">find</span> <span class="token builtin class-name">.</span> <span class="token operator">|</span> cpio -H newc -o <span class="token operator">|</span> <span class="token function">gzip</span> <span class="token operator">&gt;</span> <span class="token punctuation">..</span>/ramdisk.img
<span class="token number">4937</span> 块
</code></pre>
<h3>7. 使用 QEMU 和 Rust 支持的内核启动 Busybox 镜像</h3>
<p>进入到 <code>linux</code> 目录下，使用 <code>QEMU</code> 启动 <code>Rust</code> 支持的内核。启动的时候，把虚拟机的 <strong>23</strong> 端口映射到宿主机的 <strong>5555</strong> 端口，这样我们就可以通过 <code>telnet</code> 连接到虚拟机的 <strong>23</strong> 端口；把虚拟机的 <strong>8080</strong> 端口映射到宿主机的 <strong>5556</strong> 端口，这样可以使用 nc 命令连接到虚拟机的 <strong>Echo Server</strong> 服务。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">cd</span> linux
$ qemu-system-x86_64 -nographic -kernel vmlinux -initrd <span class="token punctuation">..</span>/busybox/ramdisk.img -nic user,model<span class="token operator">=</span>rtl8139,hostfwd<span class="token operator">=</span>tcp::5555-:23,hostfwd<span class="token operator">=</span>tcp::5556-:8080

SeaBIOS <span class="token punctuation">(</span>version <span class="token number">1.16</span>.0-debian-1.16.0-4<span class="token punctuation">)</span>

iPXE <span class="token punctuation">(</span>https://ipxe.org<span class="token punctuation">)</span> 00:03.0 CA00 PCI2.10 PnP PMM+07F8B0A0+07ECB0A0 CA00

Booting from ROM<span class="token punctuation">..</span><span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> Linux version <span class="token number">6.1</span>.0-rc1+ <span class="token punctuation">(</span>eli@eli-MI<span class="token punctuation">)</span> <span class="token punctuation">(</span>Ubuntu clang version <span class="token number">15.0</span>.2-1, Ubuntu LLD <span class="token number">15.0</span>.2<span class="token punctuation">)</span> <span class="token comment">#3 SMP Wed Nov 16 20:00:20 CST 2022</span>
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> Command line:
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> x86/fpu: x87 FPU will use FXSAVE
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> signal: max sigframe size: <span class="token number">1040</span>
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> BIOS-provided physical RAM map:
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> BIOS-e820: <span class="token punctuation">[</span>mem 0x0000000000000000-0x000000000009fbff<span class="token punctuation">]</span> usable
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> BIOS-e820: <span class="token punctuation">[</span>mem 0x000000000009fc00-0x00000000000fffff<span class="token punctuation">]</span> reserved
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> BIOS-e820: <span class="token punctuation">[</span>mem 0x0000000000100000-0x0000000007fdffff<span class="token punctuation">]</span> usable
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> BIOS-e820: <span class="token punctuation">[</span>mem 0x0000000007fe0000-0x0000000007ffffff<span class="token punctuation">]</span> reserved
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> BIOS-e820: <span class="token punctuation">[</span>mem 0x00000000fffc0000-0x00000000ffffffff<span class="token punctuation">]</span> reserved
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> NX <span class="token punctuation">(</span>Execute Disable<span class="token punctuation">)</span> protection: active
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> SMBIOS <span class="token number">2.8</span> present.
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> DMI: QEMU Standard PC <span class="token punctuation">(</span>i440FX + PIIX, <span class="token number">1996</span><span class="token punctuation">)</span>, BIOS <span class="token number">1.16</span>.0-debian-1.16.0-4 04/01/2014
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> tsc: Fast TSC calibration using PIT
<span class="token punctuation">[</span>    <span class="token number">0.000000</span><span class="token punctuation">]</span> tsc: Detected <span class="token number">1992.228</span> MHz processor

<span class="token comment"># 此处略去 1000 行</span>

<span class="token punctuation">[</span>    <span class="token number">0.689060</span><span class="token punctuation">]</span> serio: i8042 AUX port at 0x60,0x64 irq <span class="token number">12</span>
<span class="token punctuation">[</span>    <span class="token number">0.697680</span><span class="token punctuation">]</span> rust_echo_server: Hello from <span class="token builtin class-name">echo</span> server
<span class="token punctuation">[</span>    <span class="token number">0.701431</span><span class="token punctuation">]</span> NET: Registered PF_INET6 protocol family
<span class="token punctuation">[</span>    <span class="token number">0.704861</span><span class="token punctuation">]</span> input: AT Translated Set <span class="token number">2</span> keyboard as /devices/platform/i8042/serio0/input/input1
<span class="token punctuation">[</span>    <span class="token number">0.721321</span><span class="token punctuation">]</span> Segment Routing with IPv6
<span class="token punctuation">[</span>    <span class="token number">0.721716</span><span class="token punctuation">]</span> In-situ OAM <span class="token punctuation">(</span>IOAM<span class="token punctuation">)</span> with IPv6
<span class="token punctuation">[</span>    <span class="token number">0.722419</span><span class="token punctuation">]</span> sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
<span class="token punctuation">[</span>    <span class="token number">0.724941</span><span class="token punctuation">]</span> NET: Registered PF_PACKET protocol family
<span class="token punctuation">[</span>    <span class="token number">0.725245</span><span class="token punctuation">]</span> IPI shorthand broadcast: enabled
<span class="token punctuation">[</span>    <span class="token number">0.725705</span><span class="token punctuation">]</span> sched_clock: Marking stable <span class="token punctuation">(</span><span class="token number">686569475</span>, <span class="token number">38062780</span><span class="token punctuation">)</span>-<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token number">728696119</span>, -4063864<span class="token punctuation">)</span>
<span class="token punctuation">[</span>    <span class="token number">0.732047</span><span class="token punctuation">]</span> Freeing initrd memory: 1272K
<span class="token punctuation">[</span>    <span class="token number">0.769075</span><span class="token punctuation">]</span> Freeing unused kernel image <span class="token punctuation">(</span>initmem<span class="token punctuation">)</span> memory: 932K
<span class="token punctuation">[</span>    <span class="token number">0.773170</span><span class="token punctuation">]</span> Write protecting the kernel read-only data: 12288k
<span class="token punctuation">[</span>    <span class="token number">0.775963</span><span class="token punctuation">]</span> Freeing unused kernel image <span class="token punctuation">(</span>text/rodata gap<span class="token punctuation">)</span> memory: 2044K
<span class="token punctuation">[</span>    <span class="token number">0.777177</span><span class="token punctuation">]</span> Freeing unused kernel image <span class="token punctuation">(</span>rodata/data gap<span class="token punctuation">)</span> memory: 792K
<span class="token punctuation">[</span>    <span class="token number">0.777681</span><span class="token punctuation">]</span> Run /sbin/init as init process

Please press Enter to activate this console. <span class="token punctuation">[</span>    <span class="token number">1.605742</span><span class="token punctuation">]</span> tsc: Refined TSC clocksource calibration: <span class="token number">1992.216</span> MHz
<span class="token punctuation">[</span>    <span class="token number">1.607809</span><span class="token punctuation">]</span> clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x396ee9d8a25, max_idle_ns: <span class="token number">881590455116</span> ns
<span class="token punctuation">[</span>    <span class="token number">1.610399</span><span class="token punctuation">]</span> clocksource: Switched to clocksource tsc
</code></pre>
<p>在启动的过程中我们看到了 <strong>激动人心</strong> 的输出，<code>Rust</code> 的 <code>Echo Server</code> 已经启动了。</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>    <span class="token number">0.697680</span><span class="token punctuation">]</span> rust_echo_server: Hello from <span class="token builtin class-name">echo</span> server
</code></pre>
<p>让我们看看虚拟机中的网络情况。</p>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># ifconfig</span>
eth0      Link encap:Ethernet  HWaddr <span class="token number">52</span>:54:00:12:34:56
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fec0::5054:ff:fe12:3456/64 Scope:Site
          inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1248 <span class="token punctuation">(</span><span class="token number">1.2</span> KiB<span class="token punctuation">)</span>  TX bytes:1286 <span class="token punctuation">(</span><span class="token number">1.2</span> KiB<span class="token punctuation">)</span>

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 <span class="token punctuation">(</span><span class="token number">0.0</span> B<span class="token punctuation">)</span>  TX bytes:0 <span class="token punctuation">(</span><span class="token number">0.0</span> B<span class="token punctuation">)</span>

~ <span class="token comment"># wget www.google.com</span>
Connecting to www.google.com <span class="token punctuation">(</span><span class="token number">142.250</span>.72.4:80<span class="token punctuation">)</span>
Connecting to www.google.com.hk <span class="token punctuation">(</span><span class="token number">142.250</span>.72.67:80<span class="token punctuation">)</span>
Connecting to www.google.com.hk <span class="token punctuation">(</span><span class="token number">142.250</span>.72.67:80<span class="token punctuation">)</span>
saving to <span class="token string">'index.html'</span>
index.html           <span class="token number">100</span>% <span class="token operator">|</span>********************************<span class="token operator">|</span> <span class="token number">14902</span>  <span class="token number">0</span>:00:00 ETA
<span class="token string">'index.html'</span> saved
~ <span class="token comment"># rm index.html</span>
</code></pre>
<blockquote>
<p>在虚拟机中，命令提示符已经是 <strong>#</strong> ，便于和外部的命令行区分。</p>
</blockquote>
<p>从虚拟机内部连接到 <strong>Echo Server</strong> 试试。</p>
<pre class="language-bash"><code class="language-bash">~ <span class="token comment"># netstat -an</span>
Active Internet connections <span class="token punctuation">(</span>servers and established<span class="token punctuation">)</span>
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        <span class="token number">0</span>      <span class="token number">0</span> <span class="token number">0.0</span>.0.0:8080            <span class="token number">0.0</span>.0.0:*               LISTEN
tcp        <span class="token number">0</span>      <span class="token number">0</span> :::23                   :::*                    LISTEN
Active UNIX domain sockets <span class="token punctuation">(</span>servers and established<span class="token punctuation">)</span>
Proto RefCnt Flags       Type       State         I-Node Path
~ <span class="token comment"># nc 127.0.0.1 8080</span>
Hello,
Hello,
World<span class="token operator">!</span>
World<span class="token operator">!</span>
Rust <span class="token keyword">for</span> Linux
Rust <span class="token keyword">for</span> Linux
^Cpunt<span class="token operator">!</span>
</code></pre>
<p>新开一个 Terminal ，从虚拟机外部的宿主机连接进去看看。</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">nc</span> localhost <span class="token number">5556</span>
Hello,
Hello,
World<span class="token operator">!</span>
World<span class="token operator">!</span>
Rust <span class="token keyword">for</span> Linux
Rust <span class="token keyword">for</span> Linux
</code></pre>
<p>这时候我们在虚拟机内看一下网络的状态。</p>
<pre class="language-bash"><code class="language-bash">~ <span class="token comment"># netstat -an</span>
Active Internet connections <span class="token punctuation">(</span>servers and established<span class="token punctuation">)</span>
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        <span class="token number">0</span>      <span class="token number">0</span> <span class="token number">0.0</span>.0.0:8080            <span class="token number">0.0</span>.0.0:*               LISTEN
tcp        <span class="token number">0</span>      <span class="token number">0</span> <span class="token number">10.0</span>.2.15:8080          <span class="token number">10.0</span>.2.2:51558          ESTABLISHED
tcp        <span class="token number">0</span>      <span class="token number">0</span> :::23                   :::*                    LISTEN
Active UNIX domain sockets <span class="token punctuation">(</span>servers and established<span class="token punctuation">)</span>
Proto RefCnt Flags       Type       State         I-Node Path
</code></pre>
<p>可以试试使用 <code>telenet</code> 命令连接到虚拟机看是否正常。</p>
<pre class="language-bash"><code class="language-bash">$ telnet localhost <span class="token number">5555</span>
Trying <span class="token number">127.0</span>.0.1<span class="token punctuation">..</span>.
Connected to localhost.
Escape character is <span class="token string">'^]'</span><span class="token builtin class-name">.</span>

~ <span class="token comment"># ps</span>
PID   <span class="token environment constant">USER</span>     TIME  COMMAND
    <span class="token number">1</span> <span class="token number">0</span>         <span class="token number">0</span>:00 init
    <span class="token number">2</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kthreadd<span class="token punctuation">]</span>
    <span class="token number">3</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>rcu_gp<span class="token punctuation">]</span>
    <span class="token number">4</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>rcu_par_gp<span class="token punctuation">]</span>
    <span class="token number">5</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>slub_flushwq<span class="token punctuation">]</span>
    <span class="token number">6</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>netns<span class="token punctuation">]</span>
    <span class="token number">7</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/0:0-rcu<span class="token punctuation">]</span>
    <span class="token number">8</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/0:0H-ev<span class="token punctuation">]</span>
    <span class="token number">9</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/u2:0-ev<span class="token punctuation">]</span>
   <span class="token number">10</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>mm_percpu_wq<span class="token punctuation">]</span>
   <span class="token number">11</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>ksoftirqd/0<span class="token punctuation">]</span>
   <span class="token number">12</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>rcu_sched<span class="token punctuation">]</span>
   <span class="token number">13</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>migration/0<span class="token punctuation">]</span>
   <span class="token number">14</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>cpuhp/0<span class="token punctuation">]</span>
   <span class="token number">15</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kdevtmpfs<span class="token punctuation">]</span>
   <span class="token number">16</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>inet_frag_wq<span class="token punctuation">]</span>
   <span class="token number">17</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/0:1-eve<span class="token punctuation">]</span>
   <span class="token number">18</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>oom_reaper<span class="token punctuation">]</span>
   <span class="token number">19</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>writeback<span class="token punctuation">]</span>
   <span class="token number">20</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kblockd<span class="token punctuation">]</span>
   <span class="token number">21</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kswapd0<span class="token punctuation">]</span>
   <span class="token number">22</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/0:1H<span class="token punctuation">]</span>
   <span class="token number">23</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/u2:1-ev<span class="token punctuation">]</span>
   <span class="token number">24</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>acpi_thermal_pm<span class="token punctuation">]</span>
   <span class="token number">25</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/u2:2<span class="token punctuation">]</span>
   <span class="token number">26</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>mld<span class="token punctuation">]</span>
   <span class="token number">27</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>kworker/0:2-eve<span class="token punctuation">]</span>
   <span class="token number">28</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token punctuation">[</span>ipv6_addrconf<span class="token punctuation">]</span>
   <span class="token number">44</span> <span class="token number">0</span>         <span class="token number">0</span>:00 udhcpc -i eth0
   <span class="token number">45</span> <span class="token number">0</span>         <span class="token number">0</span>:00 -/bin/sh
   <span class="token number">51</span> <span class="token number">0</span>         <span class="token number">0</span>:00 telnetd -l /bin/sh
   <span class="token number">53</span> <span class="token number">0</span>         <span class="token number">0</span>:00 /bin/sh
   <span class="token number">54</span> <span class="token number">0</span>         <span class="token number">0</span>:00 <span class="token function">ps</span>
~ <span class="token comment"># ls</span>
bin          dev          etc          linuxrc      proc         ramdisk.img  root         sbin         usr
~ <span class="token comment"># uname</span>
Linux
~ <span class="token comment"># uname -a</span>
Linux <span class="token punctuation">(</span>none<span class="token punctuation">)</span> <span class="token number">6.1</span>.0-rc1+ <span class="token comment">#3 SMP Wed Nov 16 20:00:20 CST 2022 x86_64 GNU/Linux</span>
~ <span class="token comment"># kill -9 45</span>
</code></pre>
<p>这时候我们发现虚拟机的 <code>bash</code> 已经离线，需要通过 <code>Enter</code> 后重新 <code>attch</code>。大家可以试试在 <code>telnet</code> 中执行 <code>poweroff</code> 会怎么样，哈哈哈～～</p>
<h3>8. 总结</h3>
<p>目前这个环境可以支持使用 <strong>Rust</strong> 开发 <strong>Linux Kernel</strong> ，使用 Busybox 启动进行验证，后续的笔记开始分析 Echo Server 的代码，以及 sample 目录下的其它代码。</p>
<p>当然，还是强烈建议跟视频一起做一遍，这样对环境搭建的理解会更加深入。</p>
<h3>9. 参考资料</h3>
<p>[1] <a href="https://github.com/Rust-for-Linux">Rust for Linux</a></p>
<p>[2] <a href="https://www.youtube.com/watch?v=tPs1uRqOnlk">Mentorship Session: Setting Up an Environment for Writing Linux Kernel Modules in Rust</a></p>
<p>[3] <a href="https://github.com/Rust-for-Linux/linux/blob/rust/Documentation/rust/quick-start.rst">Rust for Linux - Quick Start</a></p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Rust in Kernel 6.2 的进展]]></title>
            <link>https://maquanyi.com/articles/rust-in-kernel-6-dot-2-next-round</link>
            <guid>https://maquanyi.com/articles/rust-in-kernel-6-dot-2-next-round</guid>
            <pubDate>Sun, 20 Nov 2022 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<blockquote>
<p>在一些技术媒体上看到 <a href="https://lwn.net">LWN</a> 上 <strong>Jonathan Corbet</strong> 发表了一篇 Miguel 最新提交的 28 个补丁的分析文章 <strong><a href="https://lwn.net/Articles/914458/">Rust in the 6.2 Kernel</a></strong> 。仔细搜索没有找到原文，发现这篇文章目前还是 <strong>Subscriber Only</strong> 的状态，为了抢先阅读详细内容就订阅了一年 <a href="https://lwn.net">LWN</a> 的服务。以下内容是对这篇文章的概述，如果有错误在所难免，请 TG 联系 <strong>genedna</strong> 更正。</p>
</blockquote>
<hr>
<p>在这篇文章通篇都表达了 <strong>Jonathan Corbet</strong> 认为目前 <strong>Rust for Linux</strong> 的状态是初级。 <strong>Kernel</strong> <strong>6.1</strong> 版本虽然带来 "<strong>Basic</strong>" 的支持，但是离实际应用还有很长的路要走。 <strong>Jonathan Corbet</strong> 强调 "<strong>Basic</strong>" 的支持仅限于写出 "Hello World" 级别的模块，还有很多相关的代码等待被社区 "<strong>Reviewed</strong>" 和 "<strong>merged</strong>" 到主线。<strong>Miguel Ojeda</strong> 最新提交的 <a href="https://lwn.net/ml/linux-kernel/20221110164152.26136-1-ojeda@kernel.org">28 个补丁</a>，是从 "<strong>infrastructure</strong>" 级别支持使用 <strong>Rust</strong> 编写 <strong>Linux Kernel</strong>，更加接近 "<strong>real functionality</strong>"。</p>
<p>当前并不清楚这 <a href="https://lwn.net/ml/linux-kernel/20221110164152.26136-1-ojeda@kernel.org">28 个补丁</a> 是否是为 <strong>6.2</strong> 版本合入的全部代码，虽然这和开发者期待马上使用 <strong>Rust</strong> 开发还有很大距离，但是对于 <strong>Linux Kernel</strong> 社区的 <strong>Maintainer</strong> 来说是好事，对于他们理解、<strong>Review</strong> 和合入代码是有帮助的。</p>
<blockquote>
<p><strong>The Rust-for-Linux work has been underway for a few years already; getting up to full functionality may well take a while longer yet.</strong> - _<em>Jonathan Corbet</em> 在 <a href="https://lwn.net/Articles/914458">LWN</a> 的文章中最后说到。</p>
</blockquote>
<h3>1. 支持 <strong>Kernel</strong> 8 个级别的日志输出 macro</h3>
<p>之前在 <strong>6.1</strong> 版本中，仅有 2 个 <code>macro</code> 支持 "<strong>info</strong>" 和 "<strong>emergency</strong>" 的日志输出，这次实现了 8 个完整的支持，包括 <code>pr_err!()</code>、<code>pr_warn!()</code>、<code>pr_info!()</code>、<code>pr_debug!()</code>、<code>pr_devel!()</code>、<code>pr_cont!()</code>、<code>pr_emerg!()</code>、<code>pr_alert!()</code>。这些 <code>macro</code> 的使用方式和 <strong>C</strong> 语言中的 <code>printk</code> 完全一致，只是在 <strong>Rust</strong> 中使用 <code>!</code> 代替了 <code>%</code>。</p>
<pre class="language-rust"><code class="language-rust"><span class="token keyword">use</span> <span class="token namespace">kernel<span class="token punctuation">::</span></span>pr_cont<span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token namespace">kernel<span class="token punctuation">::</span>prelude<span class="token punctuation">::</span></span><span class="token operator">*</span><span class="token punctuation">;</span>

<span class="token macro property">module!</span> <span class="token punctuation">{</span>
    <span class="token keyword">type</span><span class="token punctuation">:</span> <span class="token class-name">RustPrint</span><span class="token punctuation">,</span>
    name<span class="token punctuation">:</span> <span class="token string">b"rust_print"</span><span class="token punctuation">,</span>
    author<span class="token punctuation">:</span> <span class="token string">b"Rust for Linux Contributors"</span><span class="token punctuation">,</span>
    description<span class="token punctuation">:</span> <span class="token string">b"Rust printing macros sample"</span><span class="token punctuation">,</span>
    license<span class="token punctuation">:</span> <span class="token string">b"GPL"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>

<span class="token keyword">struct</span> <span class="token type-definition class-name">RustPrint</span><span class="token punctuation">;</span>

<span class="token keyword">impl</span> <span class="token namespace">kernel<span class="token punctuation">::</span></span><span class="token class-name">Module</span> <span class="token keyword">for</span> <span class="token class-name">RustPrint</span> <span class="token punctuation">{</span>
    <span class="token keyword">fn</span> <span class="token function-definition function">init</span><span class="token punctuation">(</span>_module<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'static</span> <span class="token class-name">ThisModule</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">Self</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
        <span class="token macro property">pr_info!</span><span class="token punctuation">(</span><span class="token string">"Rust printing macros sample (init)\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token macro property">pr_emerg!</span><span class="token punctuation">(</span><span class="token string">"Emergency message (level 0) without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_alert!</span><span class="token punctuation">(</span><span class="token string">"Alert message (level 1) without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_crit!</span><span class="token punctuation">(</span><span class="token string">"Critical message (level 2) without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_err!</span><span class="token punctuation">(</span><span class="token string">"Error message (level 3) without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_warn!</span><span class="token punctuation">(</span><span class="token string">"Warning message (level 4) without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_notice!</span><span class="token punctuation">(</span><span class="token string">"Notice message (level 5) without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_info!</span><span class="token punctuation">(</span><span class="token string">"Info message (level 6) without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token macro property">pr_info!</span><span class="token punctuation">(</span><span class="token string">"A line that"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_cont!</span><span class="token punctuation">(</span><span class="token string">" is continued"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_cont!</span><span class="token punctuation">(</span><span class="token string">" without args\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token macro property">pr_emerg!</span><span class="token punctuation">(</span><span class="token string">"{} message (level {}) with args\n"</span><span class="token punctuation">,</span> <span class="token string">"Emergency"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_alert!</span><span class="token punctuation">(</span><span class="token string">"{} message (level {}) with args\n"</span><span class="token punctuation">,</span> <span class="token string">"Alert"</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_crit!</span><span class="token punctuation">(</span><span class="token string">"{} message (level {}) with args\n"</span><span class="token punctuation">,</span> <span class="token string">"Critical"</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_err!</span><span class="token punctuation">(</span><span class="token string">"{} message (level {}) with args\n"</span><span class="token punctuation">,</span> <span class="token string">"Error"</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_warn!</span><span class="token punctuation">(</span><span class="token string">"{} message (level {}) with args\n"</span><span class="token punctuation">,</span> <span class="token string">"Warning"</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_notice!</span><span class="token punctuation">(</span><span class="token string">"{} message (level {}) with args\n"</span><span class="token punctuation">,</span> <span class="token string">"Notice"</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_info!</span><span class="token punctuation">(</span><span class="token string">"{} message (level {}) with args\n"</span><span class="token punctuation">,</span> <span class="token string">"Info"</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token macro property">pr_info!</span><span class="token punctuation">(</span><span class="token string">"A {} that"</span><span class="token punctuation">,</span> <span class="token string">"line"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_cont!</span><span class="token punctuation">(</span><span class="token string">" is {}"</span><span class="token punctuation">,</span> <span class="token string">"continued"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token macro property">pr_cont!</span><span class="token punctuation">(</span><span class="token string">" with {}\n"</span><span class="token punctuation">,</span> <span class="token string">"args"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token class-name">RustPrint</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">impl</span> <span class="token class-name">Drop</span> <span class="token keyword">for</span> <span class="token class-name">RustPrint</span> <span class="token punctuation">{</span>
    <span class="token keyword">fn</span> <span class="token function-definition function">drop</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token macro property">pr_info!</span><span class="token punctuation">(</span><span class="token string">"Rust printing macros sample (exit)\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<blockquote>
<p>关于 <strong>Linux Kernel</strong> 的日志输出，可以参考 <a href="https://docs.kernel.org/next/core-api/printk-basics.html">Message logging with printk</a>。</p>
</blockquote>
<h3>2. <code>#[vtable]</code></h3>
<p>在这次提交的 <a href="https://lwn.net/ml/linux-kernel/20221110164152.26136-1-ojeda@kernel.org">28 个补丁</a> 中，添加了相当复杂的 <code>#[vtable]</code>。 在内核中广泛使用充满函数指针的结构，文章中给了一个读写操作的一个 <code>struct</code> 作为例子。</p>
<pre class="language-c"><code class="language-C"><span class="token keyword">struct</span> <span class="token class-name">file_operations</span> <span class="token punctuation">{</span>
	<span class="token keyword">struct</span> <span class="token class-name">module</span> <span class="token operator">*</span>owner<span class="token punctuation">;</span>
	<span class="token class-name">loff_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>llseek<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span><span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>read<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">char</span> __user <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">size_t</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>write<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">const</span> <span class="token keyword">char</span> __user <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">size_t</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>read_iter<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">kiocb</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">iov_iter</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>write_iter<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">kiocb</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">iov_iter</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>iopoll<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">kiocb</span> <span class="token operator">*</span>kiocb<span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">io_comp_batch</span> <span class="token operator">*</span><span class="token punctuation">,</span>
			<span class="token keyword">unsigned</span> <span class="token keyword">int</span> flags<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>iterate<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">dir_context</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>iterate_shared<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">dir_context</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">__poll_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>poll<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">poll_table_struct</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">long</span> <span class="token punctuation">(</span><span class="token operator">*</span>unlocked_ioctl<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">long</span> <span class="token punctuation">(</span><span class="token operator">*</span>compat_ioctl<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>mmap<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">vm_area_struct</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">unsigned</span> <span class="token keyword">long</span> mmap_supported_flags<span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>open<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">inode</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>flush<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">fl_owner_t</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>release<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">inode</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>fsync<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span><span class="token punctuation">,</span> <span class="token keyword">int</span> datasync<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>fasync<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>lock<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file_lock</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>sendpage<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">page</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">,</span> <span class="token class-name">size_t</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">unsigned</span> <span class="token keyword">long</span> <span class="token punctuation">(</span><span class="token operator">*</span>get_unmapped_area<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">long</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>check_flags<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>flock<span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file_lock</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>splice_write<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">pipe_inode_info</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">size_t</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>splice_read<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">pipe_inode_info</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">size_t</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>setlease<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">long</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file_lock</span> <span class="token operator">*</span><span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">void</span> <span class="token operator">*</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">long</span> <span class="token punctuation">(</span><span class="token operator">*</span>fallocate<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span>file<span class="token punctuation">,</span> <span class="token keyword">int</span> mode<span class="token punctuation">,</span> <span class="token class-name">loff_t</span> offset<span class="token punctuation">,</span>
			  <span class="token class-name">loff_t</span> len<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>show_fdinfo<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">seq_file</span> <span class="token operator">*</span>m<span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">ifndef</span> <span class="token expression">CONFIG_MMU</span></span>
	<span class="token keyword">unsigned</span> <span class="token punctuation">(</span><span class="token operator">*</span>mmap_capabilities<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span>
	<span class="token class-name">ssize_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>copy_file_range<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span><span class="token punctuation">,</span> <span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span>
			<span class="token class-name">loff_t</span><span class="token punctuation">,</span> <span class="token class-name">size_t</span><span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token class-name">loff_t</span> <span class="token punctuation">(</span><span class="token operator">*</span>remap_file_range<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span>file_in<span class="token punctuation">,</span> <span class="token class-name">loff_t</span> pos_in<span class="token punctuation">,</span>
				   <span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span>file_out<span class="token punctuation">,</span> <span class="token class-name">loff_t</span> pos_out<span class="token punctuation">,</span>
				   <span class="token class-name">loff_t</span> len<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span> remap_flags<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>fadvise<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">file</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span><span class="token punctuation">,</span> <span class="token class-name">loff_t</span><span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>uring_cmd<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">struct</span> <span class="token class-name">io_uring_cmd</span> <span class="token operator">*</span>ioucmd<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span> issue_flags<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> __randomize_layout<span class="token punctuation">;</span>
</code></pre>
<p><code>#[vtable]</code> 的目的是在 <strong>C struct</strong> 和 <strong>Rust trait</strong> 之间进行必要的 <strong>阻抗匹配</strong> 。 <strong>阻抗匹配</strong> 是一个电学中的名词，大意是将两个不同的电路连接起来，使其能够正常工作。在这里，我觉得可以理解为将 <strong>C struct</strong> 和 <strong>Rust trait</strong> "<strong>合适</strong>" 的映射起来，使其能够正常工作。</p>
<p>在 <strong>Rust</strong> 中的一个 <code>struct</code> 如果传递给 <strong>Linux Kernel</strong> 的其他部分使用，它必须被转换为合适的 <strong>C</strong> 的 <code>struct</code> 。这个过程是由 <strong>bindgen</strong> 完成的，但是 <strong>bindgen</strong> 并不知道 <strong>Rust</strong> 的 <code>trait</code> 哪些操作已经实现，哪些操作应该用空指针表示，所以它无法将 <strong>Rust</strong> 的 <code>trait</code> 转换为 <strong>C</strong> 的 <code>struct</code>。这就是 <strong>#[vtable]</strong> 的作用，它可以为每个定义的函数生成一个特殊的常量成员，在编译时生成 C 的代码可以查询这些常量，并为缺失的操作插入空指针。 这是一个相当复杂的 <strong>macro</strong> ，在文章中也没有进行更多的解释。</p>
<h3>3. Vec 对内存申请的改进</h3>
<p><strong>Vec</strong> 是我们是我们写代码最经常用到的，这次补丁增加了 <code>try_with_capacity()</code> and <code>try_with_capacity_in()</code> 两个函数。这两个函数的作用是在 <strong>Vec</strong> 的内存申请失败时，返回 <strong>Err</strong> 而不是 panic。这样就可以在 <strong>Vec</strong> 的内存申请失败时，进行一些处理，而不是直接 <strong>panic</strong>。</p>
<h3>4. CStr 和 CString</h3>
<p>提交了两个和 <strong>C</strong> 语言代码功能一致的 <strong>CStr</strong> 和 <strong>CString</strong> ，我理解这提高了将 <strong>Rust</strong> 代码和 <strong>Kernel</strong> 其他代码进行交互的体验，这部分内容在反而评论区引起了热议，<code>#[vatable]</code> 倒是没有太多评论的声音。</p>
<hr>
<blockquote>
<p>我坚信 Rust for Linux 是这两年整个操作系统里面最大的进步，这个项目的存在，让我对 Rust 的未来充满了信心。 更多的开发者可以借助 Rust 进入到 Linux Kernel 领域，这对于 Linux Kernel 的发展是非常重要的。</p>
</blockquote>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[要不要选择 “小众不成熟” 的 Rust 作为公司技术栈？]]></title>
            <link>https://maquanyi.com/articles/should-i-choose-rust</link>
            <guid>https://maquanyi.com/articles/should-i-choose-rust</guid>
            <pubDate>Sun, 31 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="国内外对 Rust 的认知差距" loading="lazy" width="1080" height="411" decoding="async" data-nimg="1" style="color:transparent" srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frust.75d9a3e6.webp&amp;w=1080&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frust.75d9a3e6.webp&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Frust.75d9a3e6.webp&amp;w=3840&amp;q=75">
<p>周四在 OSPO Summit 现场的多位朋友都给我发来了一张图片，让我看看 Rust 是如此 "小众不成熟" 的编程语言被当作反面教育的案例，着实让我震惊。我可以想出来 100 个理由或者案例来反驳这个观点，最后我还是简单写了一句把图片发到 X 。
这几天正是 RustNation 在伦敦的会议，Google 讲到了使用 Rust 的生产效率。正好在我的页面上两个 Tweet 并排在一起，于是我截图发到朋友圈，成功引发了一众讨论。</p>
<p>这里我想表达的不是 Rust 成熟不成熟的问题，而是我们如何选择一个技术栈的思路。首先我非常同意研发团队使用某项技术（不管是不是开源）是需要纪律的，不是某团队同学自己喜好就决定使用或不使用的，但这是技术团队管理者来决定的么？</p>
<p>在国内公司的大多数团队，都缺乏一个具有商业意识、财务经验、丰富技术背景、运营经验和大局观良好的产品经理（VP），对产品思考的缺乏造成技术决策和产品设计的混乱。有的技术团队 Leader 按照自己的喜好、经验和团队的技术背景来决定技术栈，通常这种情况下选择是比较保守的，虽然风险比较低但是收益也相对较低。</p>
<p>所以我觉得选择什么技术栈是要按照商业、产品等综合因素来综合确定。任何一个公司的生存都是首要的问题，选择什么样的技术栈取决于公司的商业模式、产品形态和当前（未来）面对的商业环境情况。现在国内几家大型的云计算平台都是选择 Golang 作为主力的开发语言，字节跳动内部后端服务在大量使用 Golang ，Docker / Kubernetes 也是用 Golang 开发的。我倒是不想分析选择 Golang 的具体原因，我只想说 Golang 作为一个编程语言被选择的时候也相当的 "小众不成熟" 。</p>
<p>这个成熟不成熟的直接表现是选择所带来的商业风险大小。如果为了商业利益选择风险大的技术也是正常的，黄东旭、刘奇当年选 Golang 和 Rust 都是风险很大，PingCAP 收益如何一目了然。敢不敢选小众不成熟的技术栈考研的是决策者的能力，能不能应对带来的风险，敢不敢追求更高的收益。所以如何选择是基于商业和产品逻辑来决定的，不应是一个团队的同学、或者团队管理者单方面决定。</p>
<p>写这篇公众号文章的时候，我正在 TuGraph 在北京 Meetup 现场，TuGraph 用 Rust 编写了一个图存储的引擎 CStore 。我相信选择 Rust 的公司会越来越多，这是一个非常有前景的语言，这也是我投入在 Rust 中国生态工作的原因和信念来源。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
        <item>
            <title><![CDATA[Windows 下使用 OpenSSL 编译 Rust 项目]]></title>
            <link>https://maquanyi.com/articles/windows-openssl-rust</link>
            <guid>https://maquanyi.com/articles/windows-openssl-rust</guid>
            <pubDate>Sat, 08 Jun 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>不得不说，还是有很多人在 Windows 环境下进行开发，毕竟 Windows 下还要打游戏。 在 Windows 下进行 Rust 开发，安装 Rust 的 ToolChain 是很简单，但是稍微有点规模的项目，构建完整的编译环境就是有些折腾了，是不是这个也会劝退很多学习 Rust 的人？</p>
<p>这里就不说 Rust 的安装了，直接说 Windows 下编译 Rust 项目的 OpenSSL 问题。当然，首先你要有 C/C++ 的安装环境。最好的办法就是安装 <a href="https://visualstudio.microsoft.com/zh-hans/vs/community/">Visual Studio Community</a>，这个是免费的，安装的时候选择 C++ 的开发环境就可以了。但是非常建议你安装所有跟 C/C++ 有关的支持，谁知道编译系统的时候会遇到什么。</p>
<p>安装 OpenSSL 有很多选择，建议在 <a href="https://slproweb.com/products/Win32OpenSSL.html">The Win32/Win64 OpenSSL Installation Project</a> 下载安装最新的 Win64 版本进行安装。安装完成后，打开 “编辑系统环境变量” 的这个应用，在最下面找到 “环境变量(N)” 点击会有编辑环境变量的窗口。在 “系统变量(S)” 中点击 “新建(W)” ，添加三个环境变量。</p>
<pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">OPENSSL_DIR</span><span class="token operator">=</span>C:<span class="token punctuation">\</span>Program Files<span class="token punctuation">\</span>OpenSSL-Win64
<span class="token assign-left variable">OPENSSL_INCLUDE_DIR</span><span class="token operator">=</span>C:<span class="token punctuation">\</span>Program Files<span class="token punctuation">\</span>OpenSSL-Win64<span class="token punctuation">\</span>include
<span class="token assign-left variable">OPENSSL_LIB_DIR</span><span class="token operator">=</span>C:<span class="token punctuation">\</span>Program Files<span class="token punctuation">\</span>OpenSSL-Win64<span class="token punctuation">\</span>lib<span class="token punctuation">\</span>VC<span class="token punctuation">\</span>x64<span class="token punctuation">\</span>MD
</code></pre>
<p>最后在 'PATH' 的环境变量列表中检查 <code>C:\Windows\System32\OpenSSH\</code> 在不在其中，如果不在请添加进去，这样 OpenSSL 就配置完成了。很少会出现 <code>error: failed to run custom build command for openssl-sys v0.9.73</code> 这样的错误。或者是如下错误：</p>
<pre class="language-bash"><code class="language-bash">cargo:rerun-if-env-changed<span class="token operator">=</span>X86_64_PC_WINDOWS_MSVC_OPENSSL_LIB_DIR
cargo:rerun-if-env-changed<span class="token operator">=</span>OPENSSL_LIB_DIR
cargo:rerun-if-env-changed<span class="token operator">=</span>X86_64_PC_WINDOWS_MSVC_OPENSSL_INCLUDE_DIR
cargo:rerun-if-env-changed<span class="token operator">=</span>OPENSSL_INCLUDE_DIR
cargo:rerun-if-env-changed<span class="token operator">=</span>X86_64_PC_WINDOWS_MSVC_OPENSSL_DIR
cargo:rerun-if-env-changed<span class="token operator">=</span>OPENSSL_DIR
</code></pre>
<p>当然也可以采用 <code>vcpkg</code> 这个工具来安装 OpenSSL ，这里暂不讲述如何配置。</p>]]></content:encoded>
            <author>eli@patch.sh (马全一/马道长)</author>
        </item>
    </channel>
</rss>