<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; Windows</title>
	<atom:link href="http://tonybai.com/tag/windows/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Wed, 15 Apr 2026 23:35:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Go 2025 密码学年度报告：后量子时代的防御与 FIPS 的“纯 Go”革命</title>
		<link>https://tonybai.com/2025/11/22/the-2025-go-cryptography-state-of-the-union/</link>
		<comments>https://tonybai.com/2025/11/22/the-2025-go-cryptography-state-of-the-union/#comments</comments>
		<pubDate>Sat, 22 Nov 2025 09:47:05 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AccumulatedTestVectors]]></category>
		<category><![CDATA[AES-CTR]]></category>
		<category><![CDATA[AI原生开发工作流实战]]></category>
		<category><![CDATA[AmpereAltra]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[arm64]]></category>
		<category><![CDATA[AssemblyMutationTesting]]></category>
		<category><![CDATA[BoringSSL]]></category>
		<category><![CDATA[BorisNagaev]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[CI]]></category>
		<category><![CDATA[Constanttime]]></category>
		<category><![CDATA[crypto/internal/fips140]]></category>
		<category><![CDATA[crypto/mlkem]]></category>
		<category><![CDATA[crypto/rand.Read]]></category>
		<category><![CDATA[crypto/tls]]></category>
		<category><![CDATA[CryptographyStateoftheUnion]]></category>
		<category><![CDATA[EdgeRouter]]></category>
		<category><![CDATA[fallback]]></category>
		<category><![CDATA[FilippoValsorda]]></category>
		<category><![CDATA[FIPS]]></category>
		<category><![CDATA[FIPS合规]]></category>
		<category><![CDATA[FIPS模块]]></category>
		<category><![CDATA[FIPS模式]]></category>
		<category><![CDATA[getrandom]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.24]]></category>
		<category><![CDATA[GoBoringCrypto]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GopherCon2025]]></category>
		<category><![CDATA[GopherConUS]]></category>
		<category><![CDATA[Go团队]]></category>
		<category><![CDATA[Go语言进阶课]]></category>
		<category><![CDATA[kyber]]></category>
		<category><![CDATA[math/big]]></category>
		<category><![CDATA[MIPS]]></category>
		<category><![CDATA[ML-KEM]]></category>
		<category><![CDATA[ML-KEM768]]></category>
		<category><![CDATA[NeverFails]]></category>
		<category><![CDATA[NIST]]></category>
		<category><![CDATA[Oof]]></category>
		<category><![CDATA[Ouch]]></category>
		<category><![CDATA[RecordNowDecryptLater]]></category>
		<category><![CDATA[RollingHash]]></category>
		<category><![CDATA[RSA]]></category>
		<category><![CDATA[RSA密钥]]></category>
		<category><![CDATA[Seccomp]]></category>
		<category><![CDATA[TrailofBits]]></category>
		<category><![CDATA[vDSO]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[X25519]]></category>
		<category><![CDATA[交叉编译]]></category>
		<category><![CDATA[侧信道攻击]]></category>
		<category><![CDATA[内存安全]]></category>
		<category><![CDATA[内核]]></category>
		<category><![CDATA[协议设计缺陷]]></category>
		<category><![CDATA[双保险]]></category>
		<category><![CDATA[合规认证]]></category>
		<category><![CDATA[后量子保护]]></category>
		<category><![CDATA[后量子时代]]></category>
		<category><![CDATA[后量子签名]]></category>
		<category><![CDATA[回退逻辑]]></category>
		<category><![CDATA[复杂性]]></category>
		<category><![CDATA[安全性]]></category>
		<category><![CDATA[审计]]></category>
		<category><![CDATA[密码学]]></category>
		<category><![CDATA[密码学库]]></category>
		<category><![CDATA[工具箱]]></category>
		<category><![CDATA[工程哲学]]></category>
		<category><![CDATA[常数时间]]></category>
		<category><![CDATA[年度报告]]></category>
		<category><![CDATA[抗量子计算]]></category>
		<category><![CDATA[持续测试]]></category>
		<category><![CDATA[攻击模式]]></category>
		<category><![CDATA[敏感信息]]></category>
		<category><![CDATA[数字签名]]></category>
		<category><![CDATA[架构演进]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[格密码学]]></category>
		<category><![CDATA[椭圆曲线算法]]></category>
		<category><![CDATA[汇编代码]]></category>
		<category><![CDATA[汇编变异测试]]></category>
		<category><![CDATA[测试覆盖]]></category>
		<category><![CDATA[混合加密]]></category>
		<category><![CDATA[滚动哈希]]></category>
		<category><![CDATA[漏洞]]></category>
		<category><![CDATA[生态系统]]></category>
		<category><![CDATA[用户]]></category>
		<category><![CDATA[窃听]]></category>
		<category><![CDATA[简单]]></category>
		<category><![CDATA[累积测试向量]]></category>
		<category><![CDATA[解密]]></category>
		<category><![CDATA[计时攻击]]></category>
		<category><![CDATA[跨平台]]></category>
		<category><![CDATA[重构]]></category>
		<category><![CDATA[量子计算]]></category>
		<category><![CDATA[防御]]></category>
		<category><![CDATA[防线]]></category>
		<category><![CDATA[随机数]]></category>
		<category><![CDATA[零Go专属漏洞]]></category>
		<category><![CDATA[零漏洞审计记录]]></category>
		<category><![CDATA[零高危漏洞]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5421</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/11/22/the-2025-go-cryptography-state-of-the-union 大家好，我是Tony Bai。 2025 年 8 月，Go 官方密码学库核心维护者、Geomys 创始人 Filippo Valsorda 在 GopherCon US 上发表了备受瞩目的年度主题演讲 —— “The Go Cryptography State of the Union“。 这是一次年度技术汇报，也是一份关于 Go 语言如何应对未来十年安全挑战的战略蓝图。从抗量子计算的未雨绸缪，到 FIPS 合规的架构性重构，再到令人惊叹的“零漏洞”审计记录，Go 团队用行动证明了：最好的安全性，是让开发者无需感知、却时刻被守护的安全性。 在本文中，我们将深入解读这次演讲的核心内容，从后量子加密的技术细节到纯 Go FIPS 的实现突破，带你一窥 Go 语言构建未来安全防线的全景图。 后量子时代的第一道防线：ML-KEM 如果说量子计算是悬在现代密码学头顶的达摩克利斯之剑，那么 Go 团队已经提前为我们铸造了盾牌。 来自https://words.filippo.io/2025-state 为什么是现在？”Record Now, Decrypt Later” Filippo 开场便澄清了一个常见的误区：量子计算机可能还需要 5 到 50 年才能破解现有的非对称加密（如 RSA、ECDH），为什么我们现在就要着急？ 答案在于 “Record [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/the-2025-go-cryptography-state-of-the-union-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/11/22/the-2025-go-cryptography-state-of-the-union">本文永久链接</a> &#8211; https://tonybai.com/2025/11/22/the-2025-go-cryptography-state-of-the-union</p>
<p>大家好，我是Tony Bai。</p>
<p>2025 年 8 月，Go 官方密码学库核心维护者、Geomys 创始人 <strong>Filippo Valsorda</strong> 在 GopherCon US 上发表了备受瞩目的年度主题演讲 —— <a href="https://www.youtube.com/watch?v=YnyeAQblUyA">“The Go Cryptography State of the Union</a>“。</p>
<p>这是一次年度技术汇报，也是一份关于 Go 语言如何应对未来十年安全挑战的战略蓝图。从<a href="https://tonybai.com/2025/05/20/post-quantum-cryptography-in-go">抗量子计算的未雨绸缪</a>，到 <a href="https://tonybai.com/2024/11/16/go-crypto-and-fips-140">FIPS 合规的架构性重构</a>，再到<a href="https://tonybai.com/2025/05/21/go-crypto-audit">令人惊叹的“零漏洞”审计记录</a>，Go 团队用行动证明了：<strong>最好的安全性，是让开发者无需感知、却时刻被守护的安全性。</strong></p>
<p>在本文中，我们将深入解读这次演讲的核心内容，从后量子加密的技术细节到纯 Go FIPS 的实现突破，带你一窥 Go 语言构建未来安全防线的全景图。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-crypto-101-qr.png" alt="img{512x368}" /></p>
<hr />
<h2>后量子时代的第一道防线：ML-KEM</h2>
<p>如果说<a href="https://tonybai.com/2024/12/11/simulate-quantum-computing-in-go">量子计算</a>是悬在现代密码学头顶的达摩克利斯之剑，那么 Go 团队已经提前为我们铸造了盾牌。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/the-2025-go-cryptography-state-of-the-union-2.jpeg" alt="" /><br />
<center>来自https://words.filippo.io/2025-state</center></p>
<h3>为什么是现在？”Record Now, Decrypt Later”</h3>
<p>Filippo 开场便澄清了一个常见的误区：量子计算机可能还需要 5 到 50 年才能破解现有的非对称加密（如 RSA、ECDH），为什么我们现在就要着急？</p>
<p>答案在于 <strong>“Record Now, Decrypt Later”（现在窃听，以后解密）</strong> 的攻击模式。攻击者（或是某些国家级力量）可以现在捕获并存储加密流量，耐心等待数十年后量子计算机问世，再解密这些数据。对于长期敏感的信息（如外交电文、个人健康数据、商业机密），<strong>现在的连接已经不再安全了</strong>。</p>
<h3>Go 的应对：ML-KEM 与混合加密</h3>
<ul>
<li><strong>标准落地</strong>：Go 1.24 正式在标准库中引入了 crypto/mlkem 包，实现了 NIST 最终选定的后量子密钥交换标准 <strong>ML-KEM</strong>（即 Kyber）。</li>
<li><strong>默认开启的混合保护</strong>：最令人兴奋的是，普通开发者无需修改一行代码。在 crypto/tls 中，Go 1.24+ 默认启用了 <strong>X25519 + ML-KEM-768</strong> 的混合密钥交换模式。
<ul>
<li><strong>混合的智慧</strong>：密码学界对新算法总是保持谨慎。ML-KEM 虽然基于格密码学（Lattices），但仍可能隐藏着未知的数学缺陷。Go 团队采用了“双保险”策略：将经典的 X25519 椭圆曲线算法与 ML-KEM 结合，将两者的结果进行哈希组合。</li>
<li><strong>安全性</strong>：除非攻击者<strong>同时</strong>拥有量子计算机（破解 X25519）<strong>和</strong>破解 ML-KEM 数学结构的天才数学家，否则你的连接坚不可摧。</li>
</ul>
</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/2025/the-2025-go-cryptography-state-of-the-union-3.jpeg" alt="" /><br />
<center>来自https://words.filippo.io/2025-state</center></p>
<h3>为什么不急于“后量子签名”？</h3>
<p>与密钥交换不同，Filippo 解释了为什么后量子<strong>数字签名</strong>的推进更加缓慢。因为伪造签名需要实时进行，无法通过“现在记录，以后攻击”来实现，因此紧迫性较低。更重要的是，后量子签名的大小通常高达数 KB（相比现在的几百字节），这对网络协议设计带来了巨大的挑战，需要更多时间来演进。</p>
<hr />
<h2>FIPS 140-3：一场“纯 Go”的合规革命</h2>
<p>对于服务政府、金融或受监管行业的企业来说，<strong>FIPS 140</strong> 合规认证往往是强制性的。长期以来，Go 社区只能依赖 Go+BoringCrypto —— 一个基于 CGO 调用 Google 内部 C 语言库 BoringSSL 的方案。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/the-2025-go-cryptography-state-of-the-union-4.jpeg" alt="" /><br />
<center>来自https://words.filippo.io/2025-state</center></p>
<p>这不仅破坏了 Go 引以为傲的“静态编译、无依赖”特性，还引入了 C 代码的内存安全风险。Filippo 甚至透露，Trail of Bits 审计中发现的唯一一个真正漏洞，正是出在 Go+BoringCrypto 中。</p>
<h3>Go 1.24+ 的破局：原生 Go 模块</h3>
<p>Go 团队做出了一个大胆的决定：<strong>用纯 Go 重新实现 FIPS 模块</strong>。</p>
<ul>
<li><strong>原生与透明</strong>：新的 FIPS 模块位于 crypto/internal/fips140/&#8230;。对于用户来说，它只是标准库的一部分。当开启 FIPS 模式时，标准库会自动路由到这些经过认证的代码路径，而 API 保持完全一致。</li>
<li><strong>全平台制霸</strong>：得益于纯 Go 的跨平台特性，FIPS 支持不再局限于特定的 Linux 发行版。Filippo 自豪地展示了他在<strong>自家客厅</strong>搭建的测试实验室——从高端的 Ampere Altra ARM64 服务器，到女友的 Windows 笔记本，甚至是作为路由器的 EdgeRouter (MIPS/ARM)，全部通过了 FIPS 测试。</li>
<li><strong>无需 CGO</strong>：这是最大的胜利。开发者终于可以既拥有 FIPS 合规性，又享受 Go 原生的交叉编译和内存安全。</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/2025/the-2025-go-cryptography-state-of-the-union-5.jpeg" alt="" /><br />
<center>来自https://words.filippo.io/2025-state</center></p>
<hr />
<h2>安全记录：用测试堆出来的“零漏洞”</h2>
<p>Go 密码学库最令人骄傲的或许不是新特性，而是其惊人的安全记录。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/the-2025-go-cryptography-state-of-the-union-6.jpeg" alt="" /><br />
<center>来自https://words.filippo.io/2025-state</center></p>
<h3>惊人的成绩单</h3>
<ul>
<li><strong>零高危漏洞</strong>：自 2019 年以来，Go 密码学库未发生过任何严重（Ouch 级别）的安全漏洞。</li>
<li><strong>零 Go 专属漏洞</strong>：自 2021 年以来，甚至没有出现过 Go 实现特有的中等严重漏洞（Oof 级别）。所有出现的漏洞几乎都是协议本身的设计缺陷。</li>
<li><strong>审计背书</strong>：2025 年初，著名安全公司 <strong>Trail of Bits</strong> 对 Go 密码学库的基础设施进行了全面审计。结果令人欣慰：<strong>他们没有发现任何安全漏洞</strong>。</li>
</ul>
<h3>幕后功臣：疯狂的测试</h3>
<p>这种安全记录不是运气，而是工程化的结果：</p>
<ul>
<li><a href="https://words.filippo.io/accumulated/">累积测试向量 (Accumulated Test Vectors)</a>：如何测试一个算法在 0 到 200 字节长度的所有组合？这会产生数百万个测试用例。Go 团队使用了一种名为 <strong>“Accumulated”</strong> 的技巧：将算法在所有输入下的输出进行<strong>滚动哈希 (Rolling Hash)</strong>，最后只比对这一个哈希值。这使得在 CI 中运行海量测试成为可能。</li>
<li><a href="https://words.filippo.io/assembly-mutation/">汇编变异测试 (Assembly Mutation Testing)</a>：密码学底层大量使用汇编。为了测试难以覆盖的分支（例如进位标志的处理），团队开发了一套工具，自动<strong>“变异”</strong>汇编代码。例如，将一个“带进位加法”指令强制替换为“普通加法”。如果测试套件在汇编代码被故意破坏后依然通过，说明<strong>测试覆盖不足</strong>。这种反向验证直接消灭了潜在的盲区。</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/2025/the-2025-go-cryptography-state-of-the-union-7.jpeg" alt="" /><br />
<center>来自https://words.filippo.io/2025-state</center></p>
<hr />
<h2>细节中的魔鬼：更安全、更快的底层</h2>
<p>除了大方向的演进，无数细节的优化构成了 Go 安全的基石。Filippo 分享了几个令人印象深刻的案例：</p>
<ul>
<li><strong>RSA 的重生</strong>：crypto/rsa 包经历了彻底的重构。它不再使用通用的、性能较慢且难以防御侧信道攻击的 math/big 库，而是采用了全新的、<strong>常数时间 (Constant-time)</strong> 的底层实现。这不仅提升了性能，更从数学层面杜绝了计时攻击。同时，Go 果断<strong>移除了对小于 1024 位 RSA 密钥的支持</strong>，强制推动行业向更安全的标准迁移。</li>
<li><strong>AES-CTR 性能飞跃</strong>：通过一位社区成员 (Boris Nagaev) 的贡献，AES-CTR 模式的性能提升了 <strong>2 到 9 倍</strong>。</li>
<li><strong>永不失败的随机数</strong>：crypto/rand.Read 现在的承诺是 <strong>“Never Fails”</strong>。
<ul>
<li>在 Linux 上，它利用 vDSO 技术直接调用内核，大幅提升了获取随机数的性能。</li>
<li>为了确保承诺，团队甚至重新编写了 seccomp 库，专门用来在测试中模拟 getrandom 系统调用失败的极端场景，确保回退逻辑（fallback）绝对可靠。</li>
</ul>
</li>
</ul>
<hr />
<h2>小结：不仅要做得好，还要让开发者用得轻松</h2>
<p>Filippo Valsorda 的演讲向我们展示了 Go 语言在安全领域的宏大愿景：<strong>安全不应是开发者的负担，而应是语言赋予的基础设施。</strong></p>
<p>无论是默认开启的后量子保护，还是透明、无感的 FIPS 合规，Go 团队都在践行一种极致的工程哲学——<strong>把复杂性留给自己，把简单留给用户。</strong> 他们不满足于仅仅提供“能用”的加密算法，而是致力于通过持续的测试、审计和架构演进，为整个生态系统构筑一道坚不可摧、且能抵御未来威胁的防线。</p>
<p>随着 Go 1.24 及后续版本的发布，每一位 Gopher 手中的工具箱，都已在不知不觉中完成了升级。当我们轻松地编写代码时，Go 的密码学库正在底层默默地为我们抵挡着来自现在和未来的风暴。</p>
<hr />
<h2>参考资料</h2>
<ul>
<li><a href="https://words.filippo.io/2025-state/">Filippo Valsorda: The 2025 Go Cryptography State of the Union</a></li>
<li><a href="https://www.youtube.com/watch?v=YnyeAQblUyA">Youtube Video: GopherCon 2025 &#8211; The Go Cryptography State of the Union</a></li>
<li><a href="https://go.dev/doc/go1.24">Go 1.24 Release Notes</a></li>
<li><a href="https://words.filippo.io/accumulated/">Accumulated Test Vectors</a></li>
<li><a href="https://words.filippo.io/assembly-mutation/">Assembly Mutation Testing</a></li>
</ul>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/11/22/the-2025-go-cryptography-state-of-the-union/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go GUI 开发的“绝境”与“破局”：2025 年现状与展望</title>
		<link>https://tonybai.com/2025/11/03/go-gui-development-2025/</link>
		<comments>https://tonybai.com/2025/11/03/go-gui-development-2025/#comments</comments>
		<pubDate>Mon, 03 Nov 2025 08:52:44 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[amd64]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[API 笨重]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[arm64]]></category>
		<category><![CDATA[Bubbletea]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[C 库]]></category>
		<category><![CDATA[C 编译器]]></category>
		<category><![CDATA[C 语言共享库]]></category>
		<category><![CDATA[C/C++ GUI 框架]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[ccgo]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[CGO 痛点]]></category>
		<category><![CDATA[CGO 绑定流]]></category>
		<category><![CDATA[CGO 薄层]]></category>
		<category><![CDATA[cgo-free]]></category>
		<category><![CDATA[CLI/TUI]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[C代码转译流]]></category>
		<category><![CDATA[driver]]></category>
		<category><![CDATA[FFI]]></category>
		<category><![CDATA[FFI 方案]]></category>
		<category><![CDATA[Fyne]]></category>
		<category><![CDATA[Fysion]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Go GUI 开发]]></category>
		<category><![CDATA[Go 世界]]></category>
		<category><![CDATA[Go 初学者]]></category>
		<category><![CDATA[GoBinding]]></category>
		<category><![CDATA[GoIdiomaticAPI]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoUI]]></category>
		<category><![CDATA[GTK]]></category>
		<category><![CDATA[GUI]]></category>
		<category><![CDATA[helloworld]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaFX]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[libtk9.0]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[MaterialDesign]]></category>
		<category><![CDATA[MAUI]]></category>
		<category><![CDATA[opengl]]></category>
		<category><![CDATA[PureFFI]]></category>
		<category><![CDATA[purego]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[SelfDrawing]]></category>
		<category><![CDATA[Svelte]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Swing]]></category>
		<category><![CDATA[Tcl/Tk]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[UI 开发体验]]></category>
		<category><![CDATA[UI 控件]]></category>
		<category><![CDATA[Vue]]></category>
		<category><![CDATA[Wails]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Web 前端技术]]></category>
		<category><![CDATA[Web 技术流]]></category>
		<category><![CDATA[WebKit]]></category>
		<category><![CDATA[Webview]]></category>
		<category><![CDATA[WebView2]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[WinForms]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[wrapper]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[专业领域]]></category>
		<category><![CDATA[事件循环]]></category>
		<category><![CDATA[二进制文件]]></category>
		<category><![CDATA[云原生]]></category>
		<category><![CDATA[交叉编译]]></category>
		<category><![CDATA[交叉编译困难]]></category>
		<category><![CDATA[代码转译路径]]></category>
		<category><![CDATA[低资源占用]]></category>
		<category><![CDATA[保真度]]></category>
		<category><![CDATA[兼容性问题]]></category>
		<category><![CDATA[内存占用]]></category>
		<category><![CDATA[分发]]></category>
		<category><![CDATA[前后端逻辑]]></category>
		<category><![CDATA[前端框架]]></category>
		<category><![CDATA[功能等价]]></category>
		<category><![CDATA[动态加载]]></category>
		<category><![CDATA[动态库]]></category>
		<category><![CDATA[单一二进制文件]]></category>
		<category><![CDATA[原生 C 代码]]></category>
		<category><![CDATA[原生 GUI 框架]]></category>
		<category><![CDATA[原生 GUI 解决方案]]></category>
		<category><![CDATA[原生体验]]></category>
		<category><![CDATA[原生控件]]></category>
		<category><![CDATA[原生窗口]]></category>
		<category><![CDATA[去CGO化]]></category>
		<category><![CDATA[可维护性]]></category>
		<category><![CDATA[可读性]]></category>
		<category><![CDATA[后端]]></category>
		<category><![CDATA[后端心脏]]></category>
		<category><![CDATA[命令行工具]]></category>
		<category><![CDATA[图形用户界面]]></category>
		<category><![CDATA[外部依赖]]></category>
		<category><![CDATA[多元化]]></category>
		<category><![CDATA[学习成本]]></category>
		<category><![CDATA[完全静态]]></category>
		<category><![CDATA[官方支持]]></category>
		<category><![CDATA[展望]]></category>
		<category><![CDATA[工具链]]></category>
		<category><![CDATA[工程挑战]]></category>
		<category><![CDATA[平台原生感]]></category>
		<category><![CDATA[底层图形 API]]></category>
		<category><![CDATA[底层操作系统]]></category>
		<category><![CDATA[性能]]></category>
		<category><![CDATA[性能开销]]></category>
		<category><![CDATA[成熟工具包]]></category>
		<category><![CDATA[抽象渲染层]]></category>
		<category><![CDATA[控件实现]]></category>
		<category><![CDATA[极致功能]]></category>
		<category><![CDATA[构建依赖]]></category>
		<category><![CDATA[构建环境]]></category>
		<category><![CDATA[桌面应用]]></category>
		<category><![CDATA[桥接机制]]></category>
		<category><![CDATA[渲染机制]]></category>
		<category><![CDATA[源代码翻译器]]></category>
		<category><![CDATA[状态管理]]></category>
		<category><![CDATA[现状]]></category>
		<category><![CDATA[生态碎片化]]></category>
		<category><![CDATA[生态系统特殊性]]></category>
		<category><![CDATA[真正的 CGO-free]]></category>
		<category><![CDATA[破局]]></category>
		<category><![CDATA[破局之路]]></category>
		<category><![CDATA[社区解决方案]]></category>
		<category><![CDATA[社区驱动创新]]></category>
		<category><![CDATA[简化的构建过程]]></category>
		<category><![CDATA[纯 Go 代码]]></category>
		<category><![CDATA[组件库]]></category>
		<category><![CDATA[绑定探索]]></category>
		<category><![CDATA[绘制指令]]></category>
		<category><![CDATA[绝境]]></category>
		<category><![CDATA[维护成本高]]></category>
		<category><![CDATA[编译时]]></category>
		<category><![CDATA[编译速度慢]]></category>
		<category><![CDATA[胶水代码]]></category>
		<category><![CDATA[自动转换]]></category>
		<category><![CDATA[自绘]]></category>
		<category><![CDATA[自绘渲染流]]></category>
		<category><![CDATA[资源占用]]></category>
		<category><![CDATA[跨平台一致性]]></category>
		<category><![CDATA[转译的复杂性]]></category>
		<category><![CDATA[运行性能]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[部署]]></category>
		<category><![CDATA[部署便利性]]></category>
		<category><![CDATA[零运行时依赖]]></category>
		<category><![CDATA[非原生观感]]></category>
		<category><![CDATA[驱动层]]></category>
		<category><![CDATA[驱动适配器]]></category>
		<category><![CDATA[高性能]]></category>
		<category><![CDATA[黑魔法]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5348</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/11/03/go-gui-development-2025 大家好，我是Tony Bai。 “Go 语言能写桌面应用吗？” 这个问题，如同一个幽灵，常年盘旋在 Go 社区的上空。作为一门在后端、云原生和命令行工具领域所向披靡的语言，Go 在图形用户界面（GUI）开发上的“短板”，一直是其支持者心中一个难以言说的痛。 长期以来，Go GUI 开发似乎陷入了一种“绝境”：缺乏官方支持、生态碎片化、方案选择困难。然而，绝境之中，总有勇敢的“破局者”。社区的力量，正以多种不同的路径，顽强地探索着 Go GUI 的未来。 本文将基于当前Go社区的最新现状，为你系统性地梳理 2025 年 Go GUI 开发的几大流派，剖析其现状、权衡其利弊，并展望未来的破局之路。 “绝境”的根源：为何 Go GUI 如此之难？ 在探讨解决方案之前，我们必须先理解问题的根源。长期以来，Go GUI 开发的困境，主要源于几个核心因素： CGO 的“原罪”：几乎所有成熟的、跨平台的 GUI 工具包（如 Qt, GTK, wxWidgets）都是用 C/C++ 编写的。在 Go 中使用它们，就必须通过 CGO。这不仅打破了 Go 引以为傲的一键交叉编译能力，还带来了复杂的构建依赖和运行时的性能开销。 缺乏“亲儿子”：与 Java 的 Swing/JavaFX、.NET 的 WinForms/WPF/MAUI、或苹果生态的 SwiftUI 不同，Go 语言官方从未推出或背书过任何一个原生的 GUI 框架。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/go-gui-development-2025-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/11/03/go-gui-development-2025">本文永久链接</a> &#8211; https://tonybai.com/2025/11/03/go-gui-development-2025</p>
<p>大家好，我是Tony Bai。</p>
<p>“Go 语言能写桌面应用吗？”</p>
<p>这个问题，如同一个幽灵，常年盘旋在 Go 社区的上空。作为一门在后端、云原生和命令行工具领域所向披靡的语言，Go 在图形用户界面（GUI）开发上的“短板”，一直是其支持者心中一个难以言说的痛。</p>
<p>长期以来，Go GUI 开发似乎陷入了一种“绝境”：缺乏官方支持、生态碎片化、方案选择困难。然而，绝境之中，总有勇敢的“破局者”。社区的力量，正以多种不同的路径，顽强地探索着 Go GUI 的未来。</p>
<p>本文将基于当前Go社区的最新现状，为你系统性地梳理 2025 年 Go GUI 开发的几大流派，剖析其现状、权衡其利弊，并展望未来的破局之路。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-micro-column-2025-pr.png" alt="" /></p>
<h2>“绝境”的根源：为何 Go GUI 如此之难？</h2>
<p>在探讨解决方案之前，我们必须先理解问题的根源。长期以来，Go GUI 开发的困境，主要源于几个核心因素：</p>
<ol>
<li><strong>CGO 的“原罪”</strong>：几乎所有成熟的、跨平台的 GUI 工具包（如 Qt, GTK, <a href="https://wxwidgets.org/">wxWidgets</a>）都是用 C/C++ 编写的。在 Go 中使用它们，就必须通过 CGO。这不仅打破了 Go 引以为傲的一键交叉编译能力，还带来了复杂的构建依赖和运行时的性能开销。</li>
<li><strong>缺乏“亲儿子”</strong>：与 Java 的 Swing/JavaFX、.NET 的 WinForms/WPF/MAUI、或苹果生态的 SwiftUI 不同，Go 语言官方从未推出或背书过任何一个原生的 GUI 框架。</li>
<li><strong>生态的“碎片化”</strong>：由于缺乏官方引领，Go社区涌现出了大量解决方案，但它们路径各异、成熟度参差不齐，让开发者在选择时感到困惑和不安。</li>
</ol>
<h2>“破局”的四大流派：2025 年的现实选择</h2>
<p>尽管困难重重，但社区的探索从未停止。如今，Go GUI 的解决方案已逐渐演化为四大主流派系。</p>
<h3>流派一：Web 技术流 —— “曲线救国”的务实主义者</h3>
<p>这是目前社区中<strong>最受欢迎、也最成熟</strong>的路径。其核心思想是：<strong>放弃原生 GUI 渲染，转而利用成熟的 Web 前端技术（HTML/CSS/JS）来构建界面，同时将 Go 作为强大的后端“心脏”。</strong></p>
<ul>
<li><strong>代表项目</strong>：<a href="https://github.com/wailsapp/wails"><strong>Wails</strong></a>，目前稳定版是v2.x (go install github.com/wailsapp/wails/v2/cmd/wails@latest)。Star数量> 30K。</li>
<li><strong>工作原理</strong>：这类框架通过在原生窗口中嵌入一个 Webview（通常是操作系统自带的，如 macOS 的 WebKit，Windows 的 WebView2），来渲染前端界面。Go 程序在后端运行，并通过一套轻量级的桥接机制，将 Go 的函数和方法暴露给前端的 JavaScript 调用，反之亦然。</li>
</ul>
<p><strong>优点</strong>：</p>
<ul>
<li><strong>UI 开发体验极佳</strong>：你可以使用 React, Vue, Svelte 等任何你喜欢的前端框架，享受现代 Web 开发带来的丰富生态和高效体验。尤其适合既懂前端，又懂Go的小伙伴儿们。</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-gui-development-2025-2.png" alt="" /></p>
<ul>
<li><strong>完全摆脱 CGO</strong>：由于 Webview 是系统原生组件，整个构建过程是纯 Go 的，完美保留了 Go 的交叉编译优势。</li>
<li><strong>前后端逻辑清晰分离</strong>。</li>
</ul>
<p><strong>缺点</strong>：</p>
<ul>
<li><strong>资源占用</strong>：相比原生 GUI，Webview 会带来更高的内存占用。一个简单的“Hello World”应用，内存占用可能达到 100-200MB。</li>
<li><strong>非原生体验</strong>：虽然可以做到高度相似，但 UI 的外观和交互细节，终究与操作系统原生的控件有所差异。</li>
</ul>
<p>对于绝大多数需要构建现代化、美观界面的桌面应用，<strong>Wails 是当前 Go 社区的首选方案</strong>。它以可接受的资源开销，换来了无与伦比的开发效率和生态优势。</p>
<h3>流派二：自绘渲染流 —— Fyne 引领的“原生 Go-UI”探索</h3>
<p>这一流派的追求最为“纯粹”和“雄心勃勃”：<strong>在 Go 语言中，从头开始构建一套完整的、跨平台的 GUI 工具包。</strong> 它的核心思想不是去“绑定”一个现有的 C/C++ 框架，成为一个Go binding/wrapper，而是直接站在底层图形 API 的肩膀上，“自绘” (self-drawing) 所有的 UI 控件。这一流派的代表项目是<a href="https://github.com/fyne-io/fyne"><strong>Fyne</strong></a>。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-gui-development-2025-3.png" alt="" /></p>
<p>Fyne 的工作模式与 Web 技术流截然不同，它更接近于现代游戏引擎的渲染机制。其核心可以概括为以下几步：</p>
<ol>
<li>
<p><strong>Go 世界的 UI 描述</strong>：开发者完全使用 Go 语言来定义 UI 的结构。你通过创建 widget.NewLabel, widget.NewButton 等对象，并将它们组合在 container.NewVBox, container.NewHBox 等布局容器中，来构建你的界面树。</p>
</li>
<li>
<p><strong>抽象渲染层</strong>：Fyne 内部拥有一套名为 “Canvas” 的抽象渲染接口。当 UI 树需要被绘制时，Fyne 会将其转换为一系列与平台无关的绘制指令（如“在这里画一个矩形”、“在那里渲染一段文本”）。</p>
</li>
<li>
<p><strong>驱动层与 CGO “薄层”</strong>：这是 Fyne 与底层操作系统交互的关键。Fyne 为每个平台都实现了一个<strong>驱动 (Driver)</strong>。这个驱动的核心职责，就是将上一步中抽象的绘制指令，“翻译”成特定平台图形 API 的调用。这个“翻译”过程，正是 Fyne <strong>使用 CGO 的地方</strong>。</p>
<ul>
<li>在桌面端，它通过 CGO 调用 <strong>OpenGL</strong>（这是一个跨平台的图形标准）。</li>
<li>在移动端，它可能会调用 Android/iOS 的原生图形接口。</li>
</ul>
</li>
<li>
<p><strong>事件循环</strong>：Fyne 在后台运行一个事件循环，负责监听来自操作系统的事件（如鼠标点击、键盘输入、窗口大小改变），并将这些事件分发到 Go 世界中对应的控件上，触发你在 Go 代码中定义的响应逻辑。</p>
</li>
</ol>
<p>与<strong>CGO 绑定流</strong>（如 therecipe/qt）的UI 的所有核心逻辑——渲染、布局、事件循环——都发生在<strong>C++ 世界</strong>不同，Fyne几乎 100% 的 UI 逻辑、状态管理和控件实现，都发生在 Go 的世界里。CGO 在这里扮演的仅仅是一个薄薄的、与 GPU 对话的“驱动适配器”。</p>
<p><strong>优点</strong></p>
<ul>
<li><strong>Go-idiomatic API</strong>：Fyne 的 API 设计遵循 Go 的语言习惯，开发者可以像编写普通 Go 程序一样来构建 UI，心智负担较低。</li>
<li><strong>极致的跨平台一致性</strong>：由于所有控件都是 Fyne 自己绘制的，一个用 Fyne 编写的应用，在 Windows, macOS, Linux, Android, iOS 等所有平台上，都拥有<strong>完全一致</strong>的外观和行为。</li>
<li><strong>简化的构建过程</strong>：尽管使用了 CGO，但 Fyne 极大地简化了其构建依赖。在大多数情况下，你只需要安装好 Go 和一个 C 编译器，就可以轻松地构建跨平台应用，远比配置 Qt 或 GTK 的开发环境要简单。</li>
<li><strong>高性能与低资源占用</strong>：由于直接与 GPU 对话，其渲染性能通常很高，且最终生成的二进制文件和内存占用都非常小。</li>
</ul>
<p><strong>缺点</strong></p>
<ul>
<li><strong>非原生观感</strong>：UI 的外观是 Fyne 自定义的“Material Design”风格，与操作系统原生控件（如 macOS 的 Aqua 风格）不同。这对于某些追求“平台原生感”的应用来说，可能是一个缺点。</li>
<li><strong>生态与成熟度</strong>：虽然 Fyne 近年来发展迅速，并拥有了像 Fysion 这样的图形化编辑器，但其组件库的丰富程度、第三方工具和社区解决方案，与 Web 生态或成熟的 C++ 框架相比，仍有一定差距。</li>
</ul>
<h3>流派三：CGO 绑定流 —— 拥抱经典的“实力派”</h3>
<p>这一流派选择了最传统、也最直接的路径：<strong>通过 CGO，将 Go 语言绑定到那些久经考验的 C/C++ GUI 框架上。</strong></p>
<ul>
<li><strong>代表项目</strong>：therecipe/qt, <a href="https://github.com/gotk3/gotk3">gotk3/gotk3</a>等。</li>
<li><strong>工作原理</strong>：编写大量的 CGO “胶水代码”，将 C/C++ 框架的 API 逐一映射为 Go 的函数和类型。</li>
</ul>
<p><strong>优点</strong>：<br />
*   <strong>功能极其强大</strong>：可以直接利用 Qt, GTK 等框架数十年来积累的、极其丰富和成熟的功能与组件。<br />
*   <strong>真正的原生控件</strong>：在某些情况下（如 GTK），应用使用的是操作系统原生的 UI 控件，能提供最原汁原味的平台体验。</p>
<p><strong>缺点</strong>：<br />
*   <strong>CGO 的所有痛点</strong>：构建环境配置复杂、交叉编译困难、编译速度慢。<br />
*   <strong>API 笨重</strong>：由于是 C API 的直接映射，其使用方式可能不那么符合 Go 的语言习惯。<br />
*   <strong>维护成本高</strong>：需要持续跟进上游 C/C++ 框架的更新。</p>
<h3>流派四：C代码转译流 —— modernc.org/tk9.0 引领的“去CGO化”绑定探索</h3>
<p>在与 C/C++ GUI 框架的搏斗中，还存在着第四条、也是最“激进”的一条道路。它不满足于“薄层”的 CGO 调用，而是试图从根本上<strong>消除 C 代码本身</strong>，将其<strong>转译 (Transpile)</strong> 为纯 Go 代码。<strong>代表项目</strong>：<strong>modernc.org/tk9.0</strong>。</p>
<p>modernc.org 生态系统的作者cznic，选择了两条并行且互补的路径，来实现真正的“CGO-free”绑定：</p>
<ol>
<li>
<p><strong>Pure FFI 路径 (基于 purego)</strong>: 在 purego 支持的主流平台（如 Linux/macOS/Windows 的 amd64/arm64 架构）上，modernc.org/tk9.0 会在<strong>运行时</strong>，通过 purego 动态加载并调用系统上预装的 Tcl/Tk C 语言共享库。这与我们之前讨论的 <a href="https://tonybai.com/2025/10/23/go-ffi-new-paradigm">purego 范式</a>一致，是一种轻量级的、无 CGO 编译时依赖的 FFI 方案。</p>
</li>
<li>
<p><strong>代码转译路径 (基于 ccgo)</strong>: 这才是其真正的“黑魔法”所在。对于 purego 不支持的平台，或者在希望构建完全无外部依赖的二进制文件时，modernc.org 的作者使用了他自己开发的工具 <a href="https://gitlab.com/cznic/ccgo">ccgo</a>。ccgo 是一个 C 语言到 Go 语言的<strong>源代码翻译器</strong>。它能够读取 Tcl/Tk 的 C 源代码，并将其<strong>自动转换</strong>为功能等价的、虽然可能不那么易读的 Go 源代码，比如<a href="https://gitlab.com/cznic/libtk9.0">libtk9.0</a>。</p>
</li>
</ol>
<p><strong>优点</strong></p>
<ul>
<li><strong>真正的 CGO-free</strong>：这是它最引人注目的优点。无论目标平台如何，Go 引以为傲的一键交叉编译能力被完美地保留了下来。</li>
<li><strong>零运行时依赖（在转译模式下）</strong>：通过将 Tcl/Tk 库本身转译为 Go 代码，你的应用可以被编译成一个<strong>完全静态、不依赖于目标系统上任何共享库</strong>的单一二进制文件。这对于应用的部署和分发来说，是一个巨大的福音。</li>
<li><strong>利用成熟的工具包</strong>：开发者可以享受到 Tk 这个经过数十年考验的、极其稳定的 GUI 工具包的所有功能，而无需承受 CGO 带来的痛苦。</li>
</ul>
<p><strong>缺点</strong></p>
<ul>
<li><strong>转译的复杂性与保真度</strong>：C 到 Go 的自动转译是一个极其复杂的工程挑战。ccgo 虽然功能强大，但转译过程并非 100% 完美，可能会遇到 C 语言中某些特性的兼容性问题。</li>
<li><strong>性能与可读性</strong>：由 ccgo 生成的 Go 代码是机器生成的，其可读性和可维护性是个巨大的调整。同时，转译后的 Go 代码，其运行性能是否能与原生 C 代码媲美，也是一个需要具体场景具体测试的问题。</li>
<li><strong>生态系统特殊性</strong>：这种“转译”范式，目前是cznic 打造的modernc.org 生态系统独有的、高度集成的解决方案。选择它，意味着你需要信任并深度依赖于这个特定的、由社区英雄维护的工具链。</li>
</ul>
<h2>展望与建议：Go GUI 的破局之路在何方？</h2>
<p>Go GUI 的“绝境”，正在被社区以多元化的方式“破局”。展望 2025 年，我们不再只有一两条崎岖的小路，而是有了一幅更清晰、更多元的“路线图”。</p>
<ol>
<li>
<p><strong>Web 技术流仍是主流</strong>：在未来几年，以 <strong>Wails</strong> 为代表的 Web 技术方案，仍将是绝大多数 Go GUI 应用的最佳选择。它的生态优势和开发效率是其他方案难以比拟的。</p>
</li>
<li>
<p><strong>自绘渲染流是未来希望</strong>：<strong>Fyne</strong> 代表了 Go GUI 的“星辰大海”。随着其生态的不断成熟和完善，它有潜力成为 Go 语言未来真正的“原生” GUI 解决方案。</p>
</li>
<li>
<p><strong>CGO 绑定流是“重武器”</strong>：<strong>Qt/GTK</strong> 等传统框架的绑定，虽然沉重，但在需要极致功能和原生控件的专业领域，依然是不可或缺的“实力派”。</p>
</li>
<li>
<p><strong>C代码转译流是“黑科技”</strong>：以 <strong>modernc.org/tk9.0</strong> 为代表的转译方案，为“去 CGO 化”提供了一条全新的、激进的路径。它在部署上的巨大优势，可能会吸引越来越多的开发者关注。</p>
</li>
</ol>
<p><strong>给 Go 开发者的一些建议</strong>：</p>
<ul>
<li>如果你想快速构建一个功能丰富、界面美观的跨平台桌面应用：请毫不犹豫地选择 <strong>Wails</strong>。</li>
<li>如果你追求极致的部署便利性，并希望彻底摆脱 CGO：请深入研究 <strong>modernc.org/tk9.0</strong>。</li>
<li>如果你对性能和资源占用有极致要求，并愿意投入学习成本：请密切关注并尝试 <strong>Fyne</strong>。</li>
<li>如果你正在构建一个 CLI/TUI 应用：别忘了 <a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIyNzM0MDk0Mg==&amp;action=getalbum&amp;album_id=4108702531688333316#wechat_redirect"><strong>Bubbletea</strong></a>，它是这个领域的王者。</li>
</ul>
<p>Go GUI 的故事，是一个典型的“自下而上”的社区驱动创新的故事。虽然道阻且长，但行则将至。我们不再只有一个选择，而是可以在清晰的权衡之下，为我们的项目，找到最“恰如其分”的那条路。</p>
<p>最后，澄清一个很多Go初学者理解容易偏颇的内容，即究竟什么是”cgo-free” ？”cgo-free”的真正意思是：</p>
<ul>
<li>编译时不需要 C 编译器</li>
<li>可以交叉编译</li>
</ul>
<p>但”cgo-free”不代表程序运行时不会加载和调用对应架构的动态库(C库)。就像purego是”cgo-free”的，但使用purego的程序在运行时一般都是会调用某个依赖的C库。</p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p><strong>想系统学习Go，构建扎实的知识体系？</strong></p>
<p>我的新书《<a href="https://book.douban.com/subject/37499496/">Go语言第一课</a>》是你的首选。源自2.4万人好评的极客时间专栏，内容全面升级，同步至Go 1.24。首发期有专属五折优惠，不到40元即可入手，扫码即可拥有这本300页的Go语言入门宝典，即刻开启你的Go语言高效学习之旅！</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-primer-published-4.png" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/11/03/go-gui-development-2025/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go写业务是垃圾？Rust重写是坨屎？聊聊程序员评论区里的那股“煞气”</title>
		<link>https://tonybai.com/2025/09/19/the-tension-in-programmer-comments/</link>
		<comments>https://tonybai.com/2025/09/19/the-tension-in-programmer-comments/#comments</comments>
		<pubDate>Fri, 19 Sep 2025 00:10:06 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Bug]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Emacs]]></category>
		<category><![CDATA[GC问题]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.24]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[Go语言第一课]]></category>
		<category><![CDATA[MAC]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[Rustacean]]></category>
		<category><![CDATA[TonyBai]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[WebAssembly]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[“煞评”]]></category>
		<category><![CDATA[上下文]]></category>
		<category><![CDATA[专栏]]></category>
		<category><![CDATA[业务]]></category>
		<category><![CDATA[严谨]]></category>
		<category><![CDATA[事实]]></category>
		<category><![CDATA[二元对立]]></category>
		<category><![CDATA[优越感]]></category>
		<category><![CDATA[信仰]]></category>
		<category><![CDATA[光]]></category>
		<category><![CDATA[入门宝典]]></category>
		<category><![CDATA[公众号]]></category>
		<category><![CDATA[共同努力]]></category>
		<category><![CDATA[内容创作者]]></category>
		<category><![CDATA[净化]]></category>
		<category><![CDATA[分 享者]]></category>
		<category><![CDATA[创作者]]></category>
		<category><![CDATA[初学时期]]></category>
		<category><![CDATA[助推者]]></category>
		<category><![CDATA[化解]]></category>
		<category><![CDATA[匿名]]></category>
		<category><![CDATA[博客]]></category>
		<category><![CDATA[压力]]></category>
		<category><![CDATA[受害者]]></category>
		<category><![CDATA[同理心]]></category>
		<category><![CDATA[吐槽]]></category>
		<category><![CDATA[喷子]]></category>
		<category><![CDATA[困惑]]></category>
		<category><![CDATA[基石]]></category>
		<category><![CDATA[复杂性]]></category>
		<category><![CDATA[大规模分布式系统]]></category>
		<category><![CDATA[存在价值]]></category>
		<category><![CDATA[小型项目]]></category>
		<category><![CDATA[工程背景]]></category>
		<category><![CDATA[工程问题]]></category>
		<category><![CDATA[建设]]></category>
		<category><![CDATA[建设性]]></category>
		<category><![CDATA[建设性批评]]></category>
		<category><![CDATA[微服务]]></category>
		<category><![CDATA[心理动因]]></category>
		<category><![CDATA[思想]]></category>
		<category><![CDATA[思想碰撞]]></category>
		<category><![CDATA[思维惯性]]></category>
		<category><![CDATA[情绪]]></category>
		<category><![CDATA[情绪宣泄]]></category>
		<category><![CDATA[情绪投射]]></category>
		<category><![CDATA[戾气]]></category>
		<category><![CDATA[执念]]></category>
		<category><![CDATA[技术世界]]></category>
		<category><![CDATA[技术圣战]]></category>
		<category><![CDATA[技术方向]]></category>
		<category><![CDATA[技术社区]]></category>
		<category><![CDATA[技术观点]]></category>
		<category><![CDATA[技术讨论]]></category>
		<category><![CDATA[技术进步]]></category>
		<category><![CDATA[技术选型]]></category>
		<category><![CDATA[技术部落主义]]></category>
		<category><![CDATA[抽象能力]]></category>
		<category><![CDATA[挣扎]]></category>
		<category><![CDATA[挫败感]]></category>
		<category><![CDATA[探索]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[攻击性]]></category>
		<category><![CDATA[放大效应]]></category>
		<category><![CDATA[断言大师]]></category>
		<category><![CDATA[新人]]></category>
		<category><![CDATA[智慧]]></category>
		<category><![CDATA[替代方案]]></category>
		<category><![CDATA[最优解]]></category>
		<category><![CDATA[权衡]]></category>
		<category><![CDATA[权衡考量]]></category>
		<category><![CDATA[来源]]></category>
		<category><![CDATA[极客时间]]></category>
		<category><![CDATA[架构]]></category>
		<category><![CDATA[框架]]></category>
		<category><![CDATA[正确]]></category>
		<category><![CDATA[源码]]></category>
		<category><![CDATA[煞气]]></category>
		<category><![CDATA[珍视]]></category>
		<category><![CDATA[理性]]></category>
		<category><![CDATA[病毒]]></category>
		<category><![CDATA[知识体系]]></category>
		<category><![CDATA[知识储备]]></category>
		<category><![CDATA[知识的局限性]]></category>
		<category><![CDATA[知识的诅咒]]></category>
		<category><![CDATA[社交约束]]></category>
		<category><![CDATA[社区修行]]></category>
		<category><![CDATA[祥和之气]]></category>
		<category><![CDATA[秀优越]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[精神家园]]></category>
		<category><![CDATA[经验]]></category>
		<category><![CDATA[编辑器]]></category>
		<category><![CDATA[网络匿名性]]></category>
		<category><![CDATA[网络评论区]]></category>
		<category><![CDATA[观点]]></category>
		<category><![CDATA[讨论]]></category>
		<category><![CDATA[讨论氛围]]></category>
		<category><![CDATA[论据]]></category>
		<category><![CDATA[评论区]]></category>
		<category><![CDATA[语言]]></category>
		<category><![CDATA[读者]]></category>
		<category><![CDATA[谦逊]]></category>
		<category><![CDATA[负面情绪]]></category>
		<category><![CDATA[质疑]]></category>
		<category><![CDATA[资历]]></category>
		<category><![CDATA[资格论]]></category>
		<category><![CDATA[资深开发者]]></category>
		<category><![CDATA[身份]]></category>
		<category><![CDATA[身份认同]]></category>
		<category><![CDATA[软件开发]]></category>
		<category><![CDATA[辩论]]></category>
		<category><![CDATA[逻辑]]></category>
		<category><![CDATA[部落主义]]></category>
		<category><![CDATA[风险]]></category>
		<category><![CDATA[驱散]]></category>
		<category><![CDATA[高认知负荷]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5178</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/09/19/the-tension-in-programmer-comments 大家好，我是Tony Bai。 做公众号/博客这些年，我收到了成千上万条来自程序员朋友的评论。绝大多数都充满了智慧、好奇和善意，正是这些交流，构成了我持续分享的最大动力。但与此同时，我也常常在评论区里，感受到一股强烈的、带有攻击性的无形之气。 比如，当我分享一篇关于Go在业务场景实践的文章时，总会有人跳出来，言简意赅地留下一句：“用Go写业务是不是很垃圾？” 又比如，当社区在探讨用Rust重构某个C++项目时，评论区可能会出现这样的“高论”：“用Rust重写C++代码，就是从一坨屎变成了另一坨屎。” 这些评论，往往脏字当头，不带任何论据，纯粹是情绪的宣泄。我思来想去，觉得用“戾气”或“喷子”来形容，似乎都不够精准。直到有一天，一个词蹦进了我的脑海——“煞气”。 这个词，源于传统文化，意指一种凶戾、非理性、具有破坏性的气场。它精准地捕捉了这类评论的本质：其目的并非交流思想，而是用情绪的冲击波，扼杀讨论，打击分享者的热情。正因如此，我之前公众号的自动精选评论和留言不得不改为手工精选，这不仅增加了工作量，还降低了评论展示的及时性。 今天，这篇文章不旨在批判，而是想和大家一起，深入地聊一聊程序员评论区里的这股“煞气”，尝试理解它从何而来，并探讨作为技术社区的一员，我们该如何面对它，如何保护我们共同的精神家园。 “煞气”的百态图鉴：你一定见过的几种典型“煞评” 这股“煞气”并非铁板一块，它以多种面目出现在我们的视野中，总有一种让你觉得似曾相识： “一言以蔽之”型 这类评论堪称“断言大师”，从不屑于提供论据，仅用一句话便能给一门语言、一个框架甚至一个技术方向盖棺定论。 “Go就是不行。” “WebAssembly没前途。” “微服务就是个坑。” 简洁，有力，不容置疑，仿佛掌握了宇宙的终极真理。 “非黑即白”型（技术圣战） 在他们眼中，技术选型不是基于场景和权衡，而是一场关乎信仰的“圣战”。语言、编辑器、操作系统……万物皆可站队，异端必须被消灭。 “用Rust重写C++就是从一坨屎变成另一坨屎。” “Vim/Emacs之外皆异端！” “还在用Windows/Mac开发？笑死。” “资格论”与“秀优越”型 这类评论善于通过攻击对方的身份、资历或知识储备，来釜底抽薪式地否定其观点，从而建立自己的优越感。 “你连源码都没读过，凭什么评论？” “这东西我十年前就玩过了，没什么新意。” “等你写到百万行代码再来讨论架构吧。” “情绪投射”型 这类评论者，往往将自己在工作中因某项技术受挫而产生的负面情绪，无差别地投射到所有相关的公开讨论中，把评论区当成了情绪的垃圾桶。 “我们项目刚被XXX坑惨了，这玩意儿就是个彻头彻尾的垃圾！” “又在吹这门语言？我刚因为它的GC问题加了三天班！” 这些充满“煞气”的评论，像病毒一样侵蚀着技术社区的讨论氛围，让许多乐于分享的创作者心生寒意，也让许多渴望学习的新人望而却步。 溯源“煞气”：它们究竟从何而来？ 要应对“煞气”，首先要理解它的来源。它并非简单的“素质问题”，背后往往有更深层次的、属于程序员群体的心理动因： 高认知负荷与挫败感： 软件开发本质上是一项与复杂性搏斗的高难度、高挫败感的工作。代码不工作是常态，被需求反复折磨是日常。长期累积的压力和挫败感，需要一个宣泄的出口，而匿名的网络评论区便成了最廉价的选择。 强身份认同与技术部落主义： 许多程序员倾向于将自我价值与所掌握的技术栈深度绑定。“我是Gopher”、“我是Rustacean”，这种身份认同感带来了归属感，但也催生了“部落主义”。攻击对立的技术，本质上是在捍卫自我身份和所属部落的“荣耀”。 对“最优解”的执念与抽象能力的差异： 我们的工作是与逻辑打交道，追求严谨和正确，这使得许多程序员潜意识里相信存在一个放之四海而皆准的“最优解”。这种思维惯性，导致在面对需要权衡（Trade-off）的工程问题时，容易陷入“非黑即白”的二元对立，无法容忍不同场景下的不同选择。 知识的诅咒： 一些资深开发者，已经忘记了自己初学时期的困惑和挣扎。他们对自己领域内“显而易见”的知识缺乏同理心，容易将新手的提问或不成熟的观点视为“愚蠢”，并报以轻蔑或不耐烦。 网络匿名性的放大效应： 这是所有网络社区的通病。脱离了现实世界的社交约束，人们更容易释放出内心的攻击性。 化解“煞气”：我们每个人的社区修行 面对弥漫的“煞气”，无论是内容创作者还是普通读者，我们每个人都身处其中，既可能是受害者，也可能在不经意间成为助推者。与其抱怨环境，不如从自身做起，共同参与到社区的净化与建设中来。 给所有社区参与者的“修行建议”： 评论前，区分“观点”与“情绪”： 在敲下键盘前，花一秒钟审视内心：我即将表达的，是基于逻辑和事实的技术观点，还是仅仅是想吐槽一下今天遇到的某个Bug或者糟糕的心情？有意识地分离这两者，是理性讨论的第一步。 拥抱“建设性批评”的艺术： 如果你不同意某个观点，这非常正常，甚至是技术进步的源泉。但请尝试用建设性的方式来表达： 提供论据： “我认为这个方案有风险，因为在XX场景下，它可能会导致YY问题。” [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/the-tension-in-programmer-comments-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/09/19/the-tension-in-programmer-comments">本文永久链接</a> &#8211; https://tonybai.com/2025/09/19/the-tension-in-programmer-comments</p>
<p>大家好，我是Tony Bai。</p>
<p>做公众号/博客这些年，我收到了成千上万条来自程序员朋友的评论。绝大多数都充满了智慧、好奇和善意，正是这些交流，构成了我持续分享的最大动力。但与此同时，我也常常在评论区里，感受到一股强烈的、带有攻击性的无形之气。</p>
<p>比如，当我分享一篇关于Go在业务场景实践的文章时，总会有人跳出来，言简意赅地留下一句：“用Go写业务是不是很垃圾？”</p>
<p>又比如，当社区在探讨用Rust重构某个C++项目时，评论区可能会出现这样的“高论”：“用Rust重写C++代码，就是从一坨屎变成了另一坨屎。”</p>
<p>这些评论，往往脏字当头，不带任何论据，纯粹是情绪的宣泄。我思来想去，觉得用“戾气”或“喷子”来形容，似乎都不够精准。直到有一天，一个词蹦进了我的脑海——<strong>“煞气”</strong>。</p>
<p>这个词，源于传统文化，意指一种凶戾、非理性、具有破坏性的气场。它精准地捕捉了这类评论的本质：其目的并非交流思想，而是用情绪的冲击波，扼杀讨论，打击分享者的热情。正因如此，我之前公众号的自动精选评论和留言不得不改为手工精选，这不仅增加了工作量，还降低了评论展示的及时性。</p>
<p>今天，这篇文章不旨在批判，而是想和大家一起，<strong>深入地聊一聊程序员评论区里的这股“煞气”，尝试理解它从何而来，并探讨作为技术社区的一员，我们该如何面对它，如何保护我们共同的精神家园。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-micro-column-2025-pr.png" alt="" /></p>
<h2>“煞气”的百态图鉴：你一定见过的几种典型“煞评”</h2>
<p>这股“煞气”并非铁板一块，它以多种面目出现在我们的视野中，总有一种让你觉得似曾相识：</p>
<ul>
<li>
<p><strong>“一言以蔽之”型</strong><br />
这类评论堪称“断言大师”，从不屑于提供论据，仅用一句话便能给一门语言、一个框架甚至一个技术方向盖棺定论。</p>
<ul>
<li><em>“Go就是不行。”</em></li>
<li><em>“WebAssembly没前途。”</em></li>
<li><em>“微服务就是个坑。”</em><br />
简洁，有力，不容置疑，仿佛掌握了宇宙的终极真理。</li>
</ul>
</li>
<li>
<p><strong>“非黑即白”型（技术圣战）</strong><br />
在他们眼中，技术选型不是基于场景和权衡，而是一场关乎信仰的“圣战”。语言、编辑器、操作系统……万物皆可站队，异端必须被消灭。</p>
<ul>
<li><em>“用Rust重写C++就是从一坨屎变成另一坨屎。”</em></li>
<li><em>“Vim/Emacs之外皆异端！”</em></li>
<li><em>“还在用Windows/Mac开发？笑死。”</em></li>
</ul>
</li>
<li>
<p><strong>“资格论”与“秀优越”型</strong><br />
这类评论善于通过攻击对方的身份、资历或知识储备，来釜底抽薪式地否定其观点，从而建立自己的优越感。</p>
<ul>
<li><em>“你连源码都没读过，凭什么评论？”</em></li>
<li><em>“这东西我十年前就玩过了，没什么新意。”</em></li>
<li><em>“等你写到百万行代码再来讨论架构吧。”</em></li>
</ul>
</li>
<li>
<p><strong>“情绪投射”型</strong><br />
这类评论者，往往将自己在工作中因某项技术受挫而产生的负面情绪，无差别地投射到所有相关的公开讨论中，把评论区当成了情绪的垃圾桶。</p>
<ul>
<li><em>“我们项目刚被XXX坑惨了，这玩意儿就是个彻头彻尾的垃圾！”</em></li>
<li><em>“又在吹这门语言？我刚因为它的GC问题加了三天班！”</em></li>
</ul>
</li>
</ul>
<p>这些充满“煞气”的评论，像病毒一样侵蚀着技术社区的讨论氛围，让许多乐于分享的创作者心生寒意，也让许多渴望学习的新人望而却步。</p>
<h2>溯源“煞气”：它们究竟从何而来？</h2>
<p>要应对“煞气”，首先要理解它的来源。它并非简单的“素质问题”，背后往往有更深层次的、属于程序员群体的心理动因：</p>
<ol>
<li><strong>高认知负荷与挫败感：</strong> 软件开发本质上是一项与复杂性搏斗的高难度、高挫败感的工作。代码不工作是常态，被需求反复折磨是日常。长期累积的压力和挫败感，需要一个宣泄的出口，而匿名的网络评论区便成了最廉价的选择。</li>
<li><strong>强身份认同与技术部落主义：</strong> 许多程序员倾向于将自我价值与所掌握的技术栈深度绑定。“我是Gopher”、“我是Rustacean”，这种身份认同感带来了归属感，但也催生了“部落主义”。攻击对立的技术，本质上是在捍卫自我身份和所属部落的“荣耀”。</li>
<li><strong>对“最优解”的执念与抽象能力的差异：</strong> 我们的工作是与逻辑打交道，追求严谨和正确，这使得许多程序员潜意识里相信存在一个放之四海而皆准的“最优解”。这种思维惯性，导致在面对需要权衡（Trade-off）的工程问题时，容易陷入“非黑即白”的二元对立，无法容忍不同场景下的不同选择。</li>
<li><strong>知识的诅咒：</strong> 一些资深开发者，已经忘记了自己初学时期的困惑和挣扎。他们对自己领域内“显而易见”的知识缺乏同理心，容易将新手的提问或不成熟的观点视为“愚蠢”，并报以轻蔑或不耐烦。</li>
<li><strong>网络匿名性的放大效应：</strong> 这是所有网络社区的通病。脱离了现实世界的社交约束，人们更容易释放出内心的攻击性。</li>
</ol>
<h2>化解“煞气”：我们每个人的社区修行</h2>
<p>面对弥漫的“煞气”，无论是内容创作者还是普通读者，我们每个人都身处其中，既可能是受害者，也可能在不经意间成为助推者。与其抱怨环境，不如从自身做起，共同参与到社区的净化与建设中来。</p>
<p><strong>给所有社区参与者的“修行建议”：</strong></p>
<ol>
<li><strong>评论前，区分“观点”与“情绪”：</strong> 在敲下键盘前，花一秒钟审视内心：我即将表达的，是基于逻辑和事实的技术观点，还是仅仅是想吐槽一下今天遇到的某个Bug或者糟糕的心情？有意识地分离这两者，是理性讨论的第一步。</li>
<li><strong>拥抱“建设性批评”的艺术：</strong> 如果你不同意某个观点，这非常正常，甚至是技术进步的源泉。但请尝试用建设性的方式来表达：
<ul>
<li><strong>提供论据：</strong> “我认为这个方案有风险，因为在XX场景下，它可能会导致YY问题。”</li>
<li><strong>提供替代方案：</strong> “相比A方案，我更推荐B方案，因为B在处理XX方面更有优势。”</li>
<li><strong>补充上下文：</strong> “这个观点在小型项目中可能适用，但在大规模分布式系统中，我们需要额外考虑……”<br />
这样的评论，远比一句简单的“你这是垃圾”有价值千万倍。</li>
</ul>
</li>
<li><strong>常怀谦逊与同理心：</strong> 技术世界浩瀚无垠，我们每个人都只是其中渺小的一粟。承认自己知识的局限性，尊重不同技术在不同场景下的存在价值。我们今天所不屑的，可能正是我们昨天所困惑的；我们今天所熟稔的，可能是别人明天将要探索的新大陆。多一份同理心，少一份优越感。</li>
</ol>
<h2>小结：化“煞气”为“祥和之气”，共建更有价值的技术社区</h2>
<p>回到开头的那些评论。Go写业务当然不是垃圾，Rust重写C++也绝非原地踏步。每一种技术选择背后，都有其复杂的工程背景和权衡考量。一个健康的技术社区，应该是一个能够容纳并理性探讨这些权衡的地方。</p>
<p>我们探讨“程序员的‘煞气’”，目标不是消灭所有反对的声音，健康的质疑和辩论是技术进步的基石。我们的目标，是希望将那些无意义的、纯粹消耗热情的情绪宣泄，转化为能够推动我们共同进步的思想碰撞。</p>
<p>这需要我们每一位社区参与者的共同努力：分享者多一份对人性的理解和对经验的珍视，评论者多一份理性和建设性的态度。</p>
<p>愿我们都能成为驱散“煞气”的光，让技术社区的每一次讨论，都离智慧更近一步。</p>
<hr />
<p><strong>想系统学习Go，构建扎实的知识体系？</strong></p>
<p>我的新书《<a href="https://book.douban.com/subject/37499496/">Go语言第一课</a>》是你的首选。源自2.4万人好评的极客时间专栏，内容全面升级，同步至Go 1.24。首发期有专属五折优惠，不到40元即可入手，扫码即可拥有这本300页的Go语言入门宝典，即刻开启你的Go语言高效学习之旅！</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-primer-published-4.png" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/09/19/the-tension-in-programmer-comments/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Azure CTO 深度解读：微软为何要用 Rust “替换” C/C++，又将如何用 AI 加速代码迁移？</title>
		<link>https://tonybai.com/2025/09/11/microsoft-is-getting-rusty/</link>
		<comments>https://tonybai.com/2025/09/11/microsoft-is-getting-rusty/#comments</comments>
		<pubDate>Thu, 11 Sep 2025 13:05:49 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[AzureDataExplorer]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Crate生态系统]]></category>
		<category><![CDATA[CTO]]></category>
		<category><![CDATA[DirectWrite]]></category>
		<category><![CDATA[DISKANN]]></category>
		<category><![CDATA[GenAI]]></category>
		<category><![CDATA[GraphRAG]]></category>
		<category><![CDATA[MarkRussinovich]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Office]]></category>
		<category><![CDATA[OS]]></category>
		<category><![CDATA[ProjectMu]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[SDK]]></category>
		<category><![CDATA[Transpiler]]></category>
		<category><![CDATA[UEFI]]></category>
		<category><![CDATA[Win32k.sys]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[互操作性]]></category>
		<category><![CDATA[代码迁移]]></category>
		<category><![CDATA[内存安全]]></category>
		<category><![CDATA[内核]]></category>
		<category><![CDATA[动态链接]]></category>
		<category><![CDATA[安全未来倡议]]></category>
		<category><![CDATA[安全漏洞]]></category>
		<category><![CDATA[工具链]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[性能提升]]></category>
		<category><![CDATA[自动化代码翻译]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5148</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/09/11/microsoft-is-getting-rusty 大家好，我是Tony Bai。 近日，微软 Azure CTO、技术巨擘 Mark Russinovich 在一场 Rust 技术会议上发表了闭幕演讲，以前所未有的坦诚和力度，揭示了微软内部正在进行的一场深刻的技术变革：全面拥抱 Rust，并战略性地替代 C/C++。 他不仅分享了 Rust 在 Windows 内核、Office、Azure 云等核心产品中的惊人实践案例，还首次披露了微软正在研发的、利用 AI 大模型自动将 C/C++ 代码转换为安全 Rust 的前沿工具。这既是一次技术分享，也是一份来自行业顶层的宣言。 在这篇文章中，我们就来看看微软在走向Rust的路上究竟做了哪些工作和改变，用户和社区的反馈又是如何。 战略驱动：为何微软必须转向 Rust？ 演讲开篇，Mark Russinovich 就抛出了一个触目惊心的数据，这也是驱动微软进行这场变革的根本原因： 在过去十几年中，微软所有产品中 70% 的安全漏洞，均由 C/C++ 中的内存安全问题导致。 他直言，这种趋势仍在继续，这已不仅仅是技术债，更是持续不断的安全事件和威胁。正是基于此，他个人早已成为 Rust 的坚定拥护者，并分享了一段有趣的往事：2022年，他在看到编程语言排行榜后，有感而发地发布了一条推文——“是时候停止在任何新项目中使用 C/C++ 了，业界应该转向 Rust”。 这条推文成为了他有史以来互动量最高的内容，甚至引来了微软 CEO Satya Nadella 的电话询问。而他的回答坚定不移：“是的，我坚信如此。” 这并非一时冲动，而是一场席卷微软的、自下而上与自上而下相结合的运动。从美国国家安全局 (NSA) 呼吁业界放弃内存不安全的语言，到微软自身因不安全代码被攻击后发起的“安全未来倡议 (Secure Future Initiative)”，微软上下已经形成共识：必须摆脱不安全的语言。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/microsoft-is-getting-rusty-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/09/11/microsoft-is-getting-rusty">本文永久链接</a> &#8211; https://tonybai.com/2025/09/11/microsoft-is-getting-rusty</p>
<p>大家好，我是Tony Bai。</p>
<p>近日，微软 Azure CTO、技术巨擘 Mark Russinovich 在一场 Rust 技术会议上发表了<a href="https://www.youtube.com/watch?v=1VgptLwP588">闭幕演讲</a>，以前所未有的坦诚和力度，揭示了微软内部正在进行的一场深刻的技术变革：全面拥抱 Rust，并战略性地替代 C/C++。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/microsoft-is-getting-rusty-2.png" alt="" /></p>
<p>他不仅分享了 Rust 在 Windows 内核、Office、Azure 云等核心产品中的惊人实践案例，还首次披露了微软正在研发的、利用 AI 大模型自动将 C/C++ 代码转换为安全 Rust 的前沿工具。这既是一次技术分享，也是一份来自行业顶层的宣言。</p>
<p>在这篇文章中，我们就来看看微软在走向Rust的路上究竟做了哪些工作和改变，用户和社区的反馈又是如何。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-micro-column-2025-pr.png" alt="" /></p>
<h2>战略驱动：为何微软必须转向 Rust？</h2>
<p>演讲开篇，Mark Russinovich 就抛出了一个触目惊心的数据，这也是驱动微软进行这场变革的根本原因：</p>
<blockquote>
<p><strong>在过去十几年中，微软所有产品中 70% 的安全漏洞，均由 C/C++ 中的内存安全问题导致。</strong></p>
</blockquote>
<p>他直言，这种趋势仍在继续，这已不仅仅是技术债，更是持续不断的安全事件和威胁。正是基于此，他个人早已成为 Rust 的坚定拥护者，并分享了一段有趣的往事：2022年，他在看到编程语言排行榜后，有感而发地发布了一条推文——<strong>“是时候停止在任何新项目中使用 C/C++ 了，业界应该转向 Rust”</strong>。</p>
<p>这条推文成为了他有史以来互动量最高的内容，甚至引来了微软 CEO Satya Nadella 的电话询问。而他的回答坚定不移：“是的，我坚信如此。”</p>
<p>这并非一时冲动，而是一场席卷微软的、自下而上与自上而下相结合的运动。从美国国家安全局 (NSA) 呼吁业界放弃内存不安全的语言，到微软自身因不安全代码被攻击后发起的“安全未来倡议 (Secure Future Initiative)”，微软上下已经形成共识：<strong>必须摆脱不安全的语言</strong>。</p>
<h2>实践版图：Rust 在微软核心产品中的落地生根</h2>
<p>Mark Russinovich 随后详细介绍了 Rust 在微软内部的实践版图，其广度和深度令人瞩目。</p>
<h3>Windows：从内核“阿喀琉斯之踵”开始</h3>
<ul>
<li><strong>Project Mu (UEFI 固件):</strong> 微软选择从安全性要求极高的系统引导固件入手，用 Rust 重写了 UEFI 实现（<a href="https://github.com/microsoft/mu">Project Mu</a>）。该项目已应用于 Azure 数据中心和 Surface 笔记本，并已开源，旨在推动整个硬件生态采用 Rust。</li>
<li><strong>DirectWrite (核心图形组件):</strong> 团队选择了一个漏洞频发的独立组件——负责字体解析的 DirectWrite 进行移植。两名开发者耗时六个月，将 <strong>15.4 万行</strong> C/C++ 代码移植为 Rust。结果不仅消除了安全隐患，还意外获得了 <strong>5% 到 15% 的性能提升</strong>。</li>
<li><strong>Win32k.sys (GDI 模块):</strong> 这是 Windows 安全的“阿喀琉斯之踵”，过去20年间漏洞不断。微软选择用 Rust 重写了其中的 GDI Regions 子系统。两名开发者耗时三个月，移植了 <strong>6.3 万行</strong> 代码进入内核态。尽管 C++/Rust 的互操作边界带来了巨大挑战，但项目最终成功，且没有性能衰退。如今，在 Windows 系统目录中，你甚至能找到带有 &#95;rs 后缀的内核模块文件。</li>
</ul>
<h3>Office 与 Azure 云：性能与安全的双重胜利</h3>
<ul>
<li><strong>Office (DISKANN 向量搜索):</strong> Office 团队将一个前沿的向量搜索算法（DISKANN）从 C++ 移植到 Rust，用于 Office 365 和 Azure Cosmos DB。结果是惊人的：在实现同等 QPS 的情况下，<strong>召回率显著提升，内存占用反而下降</strong>。</li>
<li><strong>Azure (CTO 的铁腕):</strong> Mark Russinovich 透露，早在发布那条著名推文的两三年前，他就已在 Azure 内部颁布指令：<strong>“Azure 中不再有新的 C++ 系统代码”</strong>。这一指令推动了 Rust 在 Azure 基础架构中的全面应用：
<ul>
<li><strong>硬件层面:</strong> 云服务器的开源可信根项目 <strong>Caliptra</strong>、深入每台服务器的 <strong>Azure Integrated HSM</strong> 硬件安全模块，其固件均由 Rust 编写。</li>
<li><strong>硬件卸载卡:</strong> 负责网络和存储处理的智能网卡（DPU）上的新组件，已全部使用 Rust 开发，部分已有 C++ 组件也被迁移到了 Rust。</li>
<li><strong>虚拟化:</strong> Hyper-V 的 Arm64 模拟代码已用 Rust 重写；最近开源的 <strong>Open VMM</strong>（一个准虚拟化监视器）完全由 Rust 构建；而革命性的 <a href="https://github.com/hyperlight-dev/hyperlight">Hyper-V Lite 项目</a>，能以微秒级速度启动一个超轻量级虚拟机来运行 WASM 负载，其原型虽为 C#，但最终的开源实现完全是 Rust。</li>
</ul>
</li>
<li><strong>Azure 服务:</strong>
<ul>
<li><strong>Azure Data Explorer (ADX):</strong> 这个每天处理 PB 级数据的日志分析平台，其 V2 版本后完全用 <strong>35 万行 Rust 代码</strong> 重写，性能超越 C++ 版本，成为微软内部 Rust 实践的标杆案例。</li>
<li><strong>Azure SDK for Rust:</strong> 顺应客户需求，Azure 官方已发布了 Rust SDK 的 Beta 版本，标志着 Rust 正式成为 Azure 的一等公民语言。</li>
</ul>
</li>
</ul>
<h2>真实反馈：来自一线开发者的收获与挑战</h2>
<p>这场变革并非一帆风顺。Mark Russinovich 坦诚地分享了一线开发者的真实反馈：</p>
<p>** 收获 (The Positives):**</p>
<ul>
<li><strong>“如果它能编译，它就能工作”</strong>: 这是开发者们提到最多的一点，与 C++ 编译通过后仍充满不确定性的体验形成鲜明对比。</li>
<li><strong>减少摩擦，专注创新</strong>: 消除了内存安全和数据竞争等底层心智负担。</li>
<li><strong>“两个月的转变”</strong>: 一个常见的模式是，C++ 开发者最初会对所有权和借用检查器感到痛苦，但大约两个月后，他们会转变为 Rust 的忠实拥护者。</li>
</ul>
<p>** 挑战 (The Negatives):**</p>
<ul>
<li><strong>C++ 互操作性是第一大难题</strong>: 在逐步替换大型 C++ 项目时，处理两种语言的边界问题耗费了大量精力。</li>
<li><strong>工具链仍有待成熟</strong>。</li>
<li><strong>Crate 生态系统</strong>: 开发者不确定应该使用和信任哪些第三方库。</li>
<li><strong>部分依赖的特性尚未稳定</strong>。</li>
<li><strong>动态链接</strong>: 在 Windows 生态中常见的动态链接，与 Rust 的结合存在问题。</li>
</ul>
<p>尽管存在这些挑战，但 Mark Russinovich 强调，<strong>优点已经足够让微软“全身心投入 (all in)”</strong>。</p>
<h2>展望未来：用 AI 加速 “去 C++” 进程</h2>
<p>演讲的最后，Mark Russinovich 揭示了微软正在探索的、旨在加速 Rust 迁移的“终极武器”——<strong>自动化代码翻译</strong>。</p>
<p>微软正在从两个方向推进这项工作：</p>
<ol>
<li><strong>专用转译器 (Transpiler):</strong> 针对特定领域，如经过形式化验证的加密库。微软研究团队已开发出一个工具，能将严格遵循特定规范的 C 代码<strong>自动、安全地转译为 100% safe 的 Rust 代码</strong>，并确保其数学验证在转译后依然有效。</li>
<li><strong>通用 AI 翻译器 (GenAI + GraphRAG):</strong> 这是更宏伟的目标。传统的 LLM 在处理多文件、复杂的 C++ 项目时效果不佳。微软正在利用一种名为 <strong>GraphRAG</strong> (图检索增强生成) 的先进技术。该技术能将代码解析为抽象语法树，并构建一个多层次的、包含代码摘要和依赖关系的图谱。当进行翻译时，AI 可以基于这个图谱进行更精准、更具上下文感知的代码生成。</li>
</ol>
<p>他现场演示了一个将多文件 Python 小游戏翻译为 Rust 的例子。普通的 GPT-4o 生成的代码无法编译，而 <strong>GraphRAG 驱动的翻译器则一次性生成了可完美运行的、100% safe 的 Rust 代码</strong>。</p>
<h2>总结：一场自上而下的语言革命</h2>
<p>Mark Russinovich 的演讲，标志着 Rust 在主流工业界的应用进入了一个全新的阶段。微软的实践雄辩地证明，用 Rust 替代 C/C++ 不仅是为了安全，更能带来意想不到的性能收益和开发体验提升。</p>
<p>更重要的是，微软的承诺是全方位的：从内部产品的深度重构，到对社区的资金支持，再到投入研发力量攻克 C++ 互操作和自动化迁移这两大核心难题。</p>
<p>正如 Mark 所言，一门语言的成熟需要超过十年的时间。Rust 已经走到了这个节点，其生态和工具链的成熟度已经达到了一个临界点，使得像微软这样的巨头可以放心下注。对于任何想要挑战 Rust 地位的新语言来说，都将面临一座极难逾越的高山。</p>
<p>微软的“All in”，不仅是对 Rust 过去的肯定，更是对未来的巨大投资。这无疑为整个软件行业指明了一个更安全、更高效的方向。</p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/09/11/microsoft-is-getting-rusty/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>走向合规：Go加密库对FIPS 140的支持</title>
		<link>https://tonybai.com/2024/11/16/go-crypto-and-fips-140/</link>
		<comments>https://tonybai.com/2024/11/16/go-crypto-and-fips-140/#comments</comments>
		<pubDate>Fri, 15 Nov 2024 23:14:30 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AdamLangley]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[boringcrypto]]></category>
		<category><![CDATA[BoringSSL]]></category>
		<category><![CDATA[checksum]]></category>
		<category><![CDATA[CMVP]]></category>
		<category><![CDATA[CNG]]></category>
		<category><![CDATA[crypto]]></category>
		<category><![CDATA[devblog]]></category>
		<category><![CDATA[FilippoValsorda]]></category>
		<category><![CDATA[FIPS]]></category>
		<category><![CDATA[FIPS-140]]></category>
		<category><![CDATA[FIPS-140-3]]></category>
		<category><![CDATA[fork]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.23]]></category>
		<category><![CDATA[go1.8]]></category>
		<category><![CDATA[GODEBUG]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[NIST]]></category>
		<category><![CDATA[openssl]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[加密]]></category>
		<category><![CDATA[包]]></category>
		<category><![CDATA[密码学]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=4401</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2024/11/16/go-crypto-and-fips-140 在今年3月份，Microsoft Azure团队宣布开设Go开发人员博客，旨在向开发者通报Microsoft在Go领域的最新动态，包括如何在Azure上部署Go工作负载以及与Go编程相关的文章。 然而，经过一段时间的关注，我发现该博客上的大多数文章都呈现出类似下图中的标题格式： 似乎微软在紧跟Go的发布节奏，发布自己维护的fork版本。那么，这些fork版本与上游Go究竟有何不同呢？通过查阅其fork版的README文件，我们可以找到答案： 原来微软的Go分支主要是为了向开发者提供符合FIPS 140-2标准的Go加密库。 近期，Russ Cox也发起了一个新提案，旨在使Go的加密库符合FIPS 140标准，以便能够去除Boring Crypto库。 对于许多对加密领域不太熟悉的读者来说，这可能会引发一系列疑问：什么是FIPS 140标准？Go目前对FIPS 140标准的支持状态如何？新提案将如何影响Go未来对FIPS 140标准的支持？ 在这篇文章中，我们就一起了解一下FIPS 140标准、Go对其支持的现状以及未来的支持策略。 1. 什么是FIPS 140标准认证 FIPS 140（联邦信息处理标准第140号）是美国政府制定的一套计算机安全标准，主要用于规定加密模块的要求。该标准由美国国家标准与技术研究院（NIST）发布，旨在确保用于加密的硬件和软件模块满足一定的安全标准。 FIPS 140标准经历了多个版本的演进： FIPS 140-1 于1994年发布，2002年撤回。它首次定义了四个安全级别和十一项要求领域。 FIPS 140-2 于2001年发布，考虑了技术的发展和用户反馈，是国际标准ISO/IEC 19790:2006的基础文件。FIPS 140-2仍然在使用，直到2022年4月某些应用程序的测试可以继续进行。FIPS 140-2定义了四个安全级别： 级别1：最低要求，所有组件必须是“生产级”的，且不允许有明显的安全漏洞。 级别2：增加了物理防篡改的要求，要求有角色基础的身份验证。 级别3：要求更高的物理防篡改能力和基于身份的认证，同时要求对模块的关键安全参数接口进行物理或逻辑隔离。 级别4：对物理安全要求更严格，要求能够抵御环境攻击。 FIPS 140-3 在2019年发布，作为FIPS 140-2的继任者，FIPS 140-3对标准进行了更新，使其与国际标准更为一致，并引入了新的安全要求。 FIPS 140认证由加密模块验证计划（CMVP）负责，该计划是NIST与加拿大通信安全局（CSE）共同运营的。认证过程涉及对加密模块的详细测试，确保其符合相应的标准要求。所有使用加密的美国联邦政府部门都必须使用经过FIPS 140认证的模块。 FIPS 140并不保证使用该标准的模块一定是安全的，但它确立了一系列文档和测试要求，确保加密模块在设计和实现上的可靠性。对于希望使用加密模块的用户和开发者来说，确认所使用的模块是否有现有的验证证书是非常重要的。 FIPS 140是美国政府对加密模块的要求，许多公司需要遵守这些标准以满足合规性需求，尤其是一些企业在与美国政府及其他受监管行业的合作中，FIPS合规性变得至关重要，这也是微软为何要建立Go Fork分支满足FIPS合规性，以及Go团队发起尽快让Go加密库满足FIPS合规性的提案的根本原因。随着Go在受监管环境中的采用增加，FIPS合规性将影响Go的吸引力和开发者体验。 那么当前Go密码学包对FIPS的支持是怎样的呢？我们继续往下看。 2. Go密码学包对FIPS标准支持的现状 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-crypto-and-fips-140-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2024/11/16/go-crypto-and-fips-140">本文永久链接</a> &#8211; https://tonybai.com/2024/11/16/go-crypto-and-fips-140</p>
<p>在今年3月份，<a href="https://devblogs.microsoft.com/go/welcome-to-the-microsoft-for-go-developers-blog/">Microsoft Azure团队宣布开设Go开发人员博客</a>，旨在向开发者通报Microsoft在Go领域的最新动态，包括如何在Azure上部署Go工作负载以及与Go编程相关的文章。</p>
<p>然而，经过一段时间的关注，我发现该博客上的大多数文章都呈现出类似下图中的标题格式：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-crypto-and-fips-140-2.png" alt="" /></p>
<p>似乎微软在紧跟Go的发布节奏，发布<a href="https://github.com/microsoft/go">自己维护的fork版本</a>。那么，这些fork版本与上游Go究竟有何不同呢？通过<a href="https://github.com/microsoft/go/blob/microsoft/main/README.md">查阅其fork版的README文件</a>，我们可以找到答案：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-crypto-and-fips-140-3.png" alt="" /></p>
<p>原来微软的Go分支主要是为了向开发者提供符合<a href="https://en.wikipedia.org/wiki/FIPS_140">FIPS 140-2标准</a>的<a href="https://tonybai.com/2024/10/19/go-crypto-package-design-deep-dive">Go加密库</a>。</p>
<p>近期，<a href="https://github.com/golang/go/issues/70200">Russ Cox也发起了一个新提案</a>，旨在使Go的加密库符合FIPS 140标准，以便能够去除Boring Crypto库。</p>
<p>对于许多对加密领域不太熟悉的读者来说，这可能会引发一系列疑问：什么是FIPS 140标准？Go目前对FIPS 140标准的支持状态如何？新提案将如何影响Go未来对FIPS 140标准的支持？</p>
<p>在这篇文章中，我们就一起了解一下FIPS 140标准、Go对其支持的现状以及未来的支持策略。</p>
<h2>1. 什么是FIPS 140标准认证</h2>
<p>FIPS 140（联邦信息处理标准第140号）是美国政府制定的一套计算机安全标准，主要用于规定加密模块的要求。该标准由美国国家标准与技术研究院（NIST）发布，旨在确保用于加密的硬件和软件模块满足一定的安全标准。</p>
<p>FIPS 140标准经历了多个版本的演进：</p>
<ul>
<li>FIPS 140-1</li>
</ul>
<p>于1994年发布，2002年撤回。它首次定义了四个安全级别和十一项要求领域。</p>
<ul>
<li>FIPS 140-2</li>
</ul>
<p>于2001年发布，考虑了技术的发展和用户反馈，是国际标准ISO/IEC 19790:2006的基础文件。FIPS 140-2仍然在使用，直到2022年4月某些应用程序的测试可以继续进行。FIPS 140-2定义了四个安全级别：</p>
<pre><code>级别1：最低要求，所有组件必须是“生产级”的，且不允许有明显的安全漏洞。
级别2：增加了物理防篡改的要求，要求有角色基础的身份验证。
级别3：要求更高的物理防篡改能力和基于身份的认证，同时要求对模块的关键安全参数接口进行物理或逻辑隔离。
级别4：对物理安全要求更严格，要求能够抵御环境攻击。
</code></pre>
<ul>
<li>FIPS 140-3</li>
</ul>
<p>在2019年发布，作为FIPS 140-2的继任者，FIPS 140-3对标准进行了更新，使其与国际标准更为一致，并引入了新的安全要求。</p>
<p>FIPS 140认证由<strong>加密模块验证计划（CMVP）</strong>负责，该计划是NIST与加拿大通信安全局（CSE）共同运营的。认证过程涉及对加密模块的详细测试，确保其符合相应的标准要求。所有使用加密的美国联邦政府部门都必须使用经过FIPS 140认证的模块。</p>
<p>FIPS 140并不保证使用该标准的模块一定是安全的，但它确立了一系列文档和测试要求，确保加密模块在设计和实现上的可靠性。对于希望使用加密模块的用户和开发者来说，确认所使用的模块是否有现有的验证证书是非常重要的。</p>
<p>FIPS 140是美国政府对加密模块的要求，许多公司需要遵守这些标准以满足合规性需求，尤其是一些企业在与美国政府及其他受监管行业的合作中，FIPS合规性变得至关重要，这也是微软为何要建立Go Fork分支满足FIPS合规性，以及Go团队发起尽快让Go加密库满足FIPS合规性的提案的根本原因。随着Go在受监管环境中的采用增加，FIPS合规性将影响Go的吸引力和开发者体验。</p>
<p>那么当前Go密码学包对FIPS的支持是怎样的呢？我们继续往下看。</p>
<h2>2. Go密码学包对FIPS标准支持的现状</h2>
<p>到目前为止(<a href="https://mp.weixin.qq.com/s/GvPDgWP6BjxllLPNnSNS3A">Go 1.23.x版本</a>)，Go原生的crypto包并不具备FIPS认证。并且，在2017年的一个名为”<a href="https://github.com/golang/go/issues/21734">crypto: FIPS 140-2 Certification</a>“的issue中，Go密码学领域的首任技术负责人<a href="https://github.com/agl">Adam Langley</a>给出了这样的答复：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-crypto-and-fips-140-4.png" alt="" /></p>
<p>从Adam Langley的表述中可以看出，他似乎对FIPS 140这样的官方标准持有一种不屑的态度。同时他也指出对于FIPS 140认证感兴趣的开发者，可以尝试使用<a href="https://github.com/golang/go/tree/dev.boringcrypto">dev.boringcrypto分支</a>。</p>
<p>dev.boringcrypto分支是什么呢？它又是如何实现FPIS 140合规的呢？其实思路很简单，那就是：<strong>既然我暂时是不合规的，那我就找一个合规的，然后包装一下提供给开发者使用</strong>。</p>
<p>那么什么库是合规的呢？<a href="https://github.com/google/boringssl">BoringSSL</a>，也就是Google fork的OpenSSL库的自维护版本，更精确地一点是BoringSSL库的一部分内容通过了<strong>FIPS 140-2认证</strong>：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-crypto-and-fips-140-6.png" alt="" /><br />
<center>截图来自Google Cloud blog</center></p>
<p>而通过认证的这部分模块被称为<a href="https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407">BoringCrypto</a>：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-crypto-and-fips-140-7.png" alt="" /><br />
<center>截图来自NIST官网</center></p>
<p>Go dev.boringcrypto分支就是通过BoringSSL的binding来实现FIPS 140合规的，通过导入crypto/tls/fipsonly包，可以将所有TLS配置限制为FIPS合规设置，确保使用合规的加密算法。下面是Go 1.23.0中crypto/tls/fipsonly下fipsonly.go的源码，我们可以看到它实际上使用的是crypto/internal/boring下面的合规包：</p>
<pre><code>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build boringcrypto

// Package fipsonly restricts all TLS configuration to FIPS-approved settings.
//
// The effect is triggered by importing the package anywhere in a program, as in:
//
//  import _ "crypto/tls/fipsonly"
//
// This package only exists when using Go compiled with GOEXPERIMENT=boringcrypto.
package fipsonly

// This functionality is provided as a side effect of an import to make
// it trivial to add to an existing program. It requires only a single line
// added to an existing source file, or it can be done by adding a whole
// new source file and not modifying any existing source files.

import (
    "crypto/internal/boring/fipstls"
    "crypto/internal/boring/sig"
)

func init() {
    fipstls.Force()
    sig.FIPSOnly()
}
</code></pre>
<p>从<a href="https://tonybai.com/2017/02/03/some-changes-in-go-1-8/">Go 1.8</a>开始，Go都会在发布大版本时建立对应该大版本的dev.boriingcrypto分支，如下图：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-crypto-and-fips-140-5.png" alt="" /></p>
<p>但Go官方在<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go 1.18版本</a>之后似乎就不维护这个分支了。微软也恰是从那时(2022年初)开始fork了Go repo并维护自己的<a href="https://github.com/microsoft/go/tree/microsoft/main/eng/doc/fips">fips合规版本</a>，在Linux上，该Fork使用<a href="https://github.com/golang-fips/openssl">OpenSSL的Go binding</a>进行加密，而在Windows上使用CNG（Cryptography Next Generation）。</p>
<blockquote>
<p>注：尽管使用微软的Go工具链构建的应用程序可以在FIPS兼容模式下运行，但这并不意味着自动符合FIPS认证。开发团队需确保使用FIPS合规的加密原语及工作流程。如果无法提供FIPS合规的实现，修改后的加密运行时将回退到Go标准库的加密方法，如使用crypto/md5或非标准nonce大小的AES-GCM等。</p>
</blockquote>
<p>由于BoringSSL是C语言的，Go dev.boringcrypto分支势必要依赖cgo，将部分加密包的内部实现替换为通过FIPS 140认证的模块。但这种方案存在许多问题，如内存不安全代码、影响Go版本更新、性能问题和开发体验等。于是就有了文前提到的旨在移除BoringCrypto的Go团队的新提案。</p>
<p>新提案的内容是什么呢？下面我们就来细致看看。</p>
<h2>3. Go加密库原生支持FIPS 140认证的提案</h2>
<p>根据Go加密库上一任Tech leader Filippo Valsorda在<a href="https://github.com/golang/go/issues/70123">proposal: crypto: mechanism to enable FIPS mode</a>中的描述，Go团队希望为Go加密库实现FIPS 140-3认证，并允许开发者启用或禁用FIPS模式，以满足合规性要求。</p>
<p>该proposal建议在运行时通过设置GODEBUG标志来启用FIPS模式，新增GODEBUG=fips140选项。并且通过GODEBUG=fips140的值可以控制FIPS模式：</p>
<ul>
<li>on为启用FIPS模式。</li>
<li>only：仅允许使用经过批准的加密算法，其他非批准算法将返回错误。</li>
<li>enforce（该值依然在讨论中）：它会强制执行使用FIPS合规算法，非批准算法将返回错误或导致程序崩溃。</li>
</ul>
<p>在代码层面，新增crypto/fips140包或放在crypto/internal/fips下，其中包含Enabled() bool函数，用于运行时检查当前是否启用了FIPS模式。</p>
<p>Russ Cox之后在”<a href="https://github.com/golang/go/issues/70200">proposal: cmd/go: add fips140 module selection mechanism</a>“的issue中着重阐述了在Go module层面对fips 140的支持策略，目前仍在更新中，根据Russ Cox 2024.11.11的最新comment，当前设计的策略大致如下：</p>
<ul>
<li>引入新的目标配置环境变量GOFIPS140用于构建工具链（替代之前在propsal中考虑新增的-fips140命令行标志）</li>
</ul>
<p>该环境变量可取值为off、latest、inprocess、certified或v1.X.Y-fips.N，默认值为off。使用go version -m或获取debug.BuildInfo时，可显示新的构建设置GOFIPS140，其值为off、devel或具体的版本号。</p>
<pre><code>- off（默认）：使用最新源代码，GODEBUG设置为fips140=off。
- latest：使用最新源代码，GODEBUG设置为fips140=on。
- v1.X.Y-fips.N：使用指定的快照。
- inprocess：使用正在进行FIPS标准认证的版本。
- certified：使用经过NIST的FIPS认证的版本。
</code></pre>
<ul>
<li>不将FIPS代码视为module</li>
</ul>
<p>用户不可见的API或工具将不会将crypto/internal/fips代码视为module。在运行go list时，crypto/internal/fips/&#8230;的包可以来自\$GOROOT/src/crypto/internal/fips/&#8230;或module cache中的目录，Module字段将为nil，与标准库其他部分一致。</p>
<ul>
<li>版本管理与模块系统的分离</li>
</ul>
<p>尽管crypto/internal/fips有语义版本控制的版本集合，但它们与Go模块系统完全分离。存在于lib/fips140中的文件将采用实现定义(implementation-defined)的格式，尽管其格式很可能采用module zip和checksum的形式。</p>
<p>以上策略的实施将增强Go在FIPS 140支持方面的灵活性和可控性，为开发者提供了更清晰的配置选项。通过将FIPS 支持的配置独立于模块系统，开发者可以更方便地管理构建环境，避免潜在的配置冲突。</p>
<p>并且按照Russ Cox的说法，Go团队计划<strong>每年进行一次Go加密库的重新验证</strong>，以保持模块的合规性和及时更新。</p>
<h2>4. 小结</h2>
<p>本文探讨了Go语言加密库在FIPS 140标准支持方面的现状及未来发展。FIPS 140是美国政府制定的一套加密模块安全标准，目前的版本为FIPS 140-3，涵盖了最新的安全要求。</p>
<p>我们详细分析了<a href="https://mp.weixin.qq.com/s/60eD5gN_QApnGkwITZRMpw">Go加密库的现状</a>，包括通过BoringSSL实现FIPS 140合规的dev.boringcrypto分支，以及微软维护的FIPS 140合规Go Fork版本。此外，Go团队还提出了针对FIPS 140-3认证的新提案，旨在允许开发者在运行时灵活启用或禁用FIPS模式。这一新提案的实施将为Go提供更大的合规灵活性，并为开发者提供清晰的配置选项，从而增强Go在受监管环境中的吸引力和实用性。</p>
<p>目前，关于Go加密库对FIPS 140支持的相关事项仍<strong>处于提案阶段</strong>，具体思路和实现细节可能会随着进一步的发展而变化。然而，本文通过对FIPS认证的介绍以及Go加密库未来计划的阐述，相信读者已经初步掌握了选择Go加密模块和满足合规性需求的有用信息。</p>
<p>如果你有什么疑问，欢迎在评论区留言，通过讨论碰撞，我们一起进步和成长！</p>
<h2>5. 参考资料</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/FIPS_140">FIPS 140</a> &#8211; https://en.wikipedia.org/wiki/FIPS_140</li>
<li><a href="https://github.com/golang/go/issues/69536">crypto: obtain a FIPS 140-3 validation</a> &#8211; https://github.com/golang/go/issues/69536</li>
<li><a href="https://github.com/golang/go/issues/70123">proposal: crypto: mechanism to enable FIPS mode</a> &#8211; https://github.com/golang/go/issues/70123</li>
<li><a href="https://github.com/golang/go/issues/70200">proposal: cmd/go: add fips140 module selection mechanism</a> &#8211; https://github.com/golang/go/issues/70200</li>
<li><a href="https://github.com/microsoft/go/blob/microsoft/main/eng/doc/fips/README.md">Crypto FIPS 140-2 support</a> &#8211; https://github.com/microsoft/go/blob/microsoft/main/eng/doc/fips/README.md</li>
<li><a href="https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md">BoringSSL FIPS 140-2</a> &#8211; https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/FIPS.md</li>
<li><a href="https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407">CMVP: Certificate #4407</a> &#8211;  https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407</li>
<li><a href="https://cloud.google.com/security/compliance/fips-140-2-validated">FIPS 140-2 Validated</a> &#8211; https://cloud.google.com/security/compliance/fips-140-2-validated</li>
<li><a href="https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp3678.pdf">Google, LLC BoringCrypto FIPS 140-2 Non-Proprietary Security Policy</a> &#8211; https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp3678.pdf</li>
</ul>
<hr />
<p><a href="https://public.zsxq.com/groups/51284458844544">Gopher部落知识星球</a>在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时，我们也会加强代码质量和最佳实践的分享，包括如何编写简洁、可读、可测试的Go代码。此外，我们还会加强星友之间的交流和互动。欢迎大家踊跃提问，分享心得，讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落，享受coding的快乐! 欢迎大家踊跃加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻) &#8211; https://gopherdaily.tonybai.com</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>Gopher Daily归档 &#8211; https://github.com/bigwhite/gopherdaily</li>
<li>Gopher Daily Feed订阅 &#8211; https://gopherdaily.tonybai.com/feed</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2024, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2024/11/16/go-crypto-and-fips-140/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>十分钟入门Go语言</title>
		<link>https://tonybai.com/2023/02/23/learn-go-in-10-min/</link>
		<comments>https://tonybai.com/2023/02/23/learn-go-in-10-min/#comments</comments>
		<pubDate>Thu, 23 Feb 2023 14:16:52 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Array]]></category>
		<category><![CDATA[As]]></category>
		<category><![CDATA[Bool]]></category>
		<category><![CDATA[byte]]></category>
		<category><![CDATA[cap]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[Cillum]]></category>
		<category><![CDATA[closure]]></category>
		<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[Const]]></category>
		<category><![CDATA[defer]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[float64]]></category>
		<category><![CDATA[for]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go.mod]]></category>
		<category><![CDATA[go1.11]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOPATH]]></category>
		<category><![CDATA[GOPROXY]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[Go语义第一课]]></category>
		<category><![CDATA[if]]></category>
		<category><![CDATA[import]]></category>
		<category><![CDATA[InfluxDB]]></category>
		<category><![CDATA[int]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[IO]]></category>
		<category><![CDATA[Is]]></category>
		<category><![CDATA[KenThompson]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[len]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[pprof]]></category>
		<category><![CDATA[prometheus]]></category>
		<category><![CDATA[range]]></category>
		<category><![CDATA[reader]]></category>
		<category><![CDATA[receiver]]></category>
		<category><![CDATA[RobertGriesemer]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[runtime]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[select]]></category>
		<category><![CDATA[semver]]></category>
		<category><![CDATA[Slice]]></category>
		<category><![CDATA[String]]></category>
		<category><![CDATA[switch]]></category>
		<category><![CDATA[type]]></category>
		<category><![CDATA[typeparameter]]></category>
		<category><![CDATA[var]]></category>
		<category><![CDATA[variables]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[writer]]></category>
		<category><![CDATA[依赖管理]]></category>
		<category><![CDATA[函数]]></category>
		<category><![CDATA[切片]]></category>
		<category><![CDATA[变量]]></category>
		<category><![CDATA[常量]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[指针]]></category>
		<category><![CDATA[接口]]></category>
		<category><![CDATA[控制语句]]></category>
		<category><![CDATA[数组]]></category>
		<category><![CDATA[方法]]></category>
		<category><![CDATA[极客时间]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[类型]]></category>
		<category><![CDATA[类型断言]]></category>
		<category><![CDATA[类型约束]]></category>
		<category><![CDATA[结构体]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[语义版本]]></category>
		<category><![CDATA[运行时]]></category>
		<category><![CDATA[闭包]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3808</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2023/02/23/learn-go-in-10-min 本文旨在带大家快速入门Go语言，期望小伙伴们在花费十分钟左右通读全文后能对Go语言有一个初步的认知，为后续进一步深入学习Go奠定基础。 本文假设你完全没有接触过Go，你可能是一名精通其他编程语言的程序员，也可能是毫无编程经验、刚刚想转行为码农的热血青年。 编程简介 编程就是生产可在计算机上执行的程序的过程(如下图)。在这个过程中，程序员是“劳动力”，编程语言是工具，可执行的程序是生产结果。而Go语言就是程序员在编程生产过程中使用的一种优秀生产工具。 作为“劳动力”的程序员在这个过程中要做的就是使用某种编程语言作为生产工具，将事先设计好的执行逻辑组织和表达出来，这与一个作家将其大脑中设计好的故事情节用人类语言组织和书写在纸上的过程颇为类似(如下图)。 通过这个类比来看，学习一门编程语言，就好比学习一门人类语言，其词汇和语法将是我们的主要学习内容，本文就将围绕Go语言的主要“词汇”和语法形式进行快速说明。 Go简介 Go语言是由Google公司的三位大神级程序员Robert Griesemer、Rob Pike和Ken Thompson在2007年共同开发的一种新的后端编程语言，2009年，Go语言宣布开源。 Go语言的特点是简单易学、静态类型、编译速度快，运行效率高，代码简洁，并且原生支持并发编程。它还支持自动内存管理，可以让开发者更加专注于编程本身，而不用担心内存泄漏的问题。此外，Go语言还支持多核处理器，可以更好地利用多核处理器的优势，提高程序的运行效率。 经过十多年的发展，Go语言现在已经成为一种流行的编程语言，它可以用于开发各种应用程序，包括Web应用、网络服务、系统管理工具、移动应用、游戏开发、数据库管理等。Go语言常用于构建大型分布式系统，以及构建高性能的服务器端应用程序。Go为当前的云原生计算时代开发了一批“杀手级”应用，包括Docker、Kubernetes、Prometheus、InfluxDB、Cilium等。 安装Go Go是静态语言，需要先编译，再执行，因此在开发Go程序之前，我们首先需要安装Go编译器以及相关工具链。安装的步骤很简单： 从Go官网下载最新版本的Go语言安装包 &#8211; https://go.dev/dl/ 解压安装包，并将其复制到您想要安装的位置，例如：/usr/local/go；如果是Windows、MacOS平台，也可以下载图形化安装的安装包； 设置环境变量，将Go语言的安装路径添加到PATH变量中； 打开终端，输入go version，检查Go语言是否安装成功。如输出类似下面的内容，则表明安装成功！ $go version go version go1.20 darwin/amd64 注：位于中国大陆的开发者们还需要一个额外的设置：export GOPROXY=&#8217;https://goproxy.cn&#8217;或将这个设置置于shell配置文件(比如.bashrc)中并使之生效。 第一个Go程序：Hello World 建立一个新目录，并在其中创建新文件helloworld.go，用任意编辑器打开helloworld.go，输入下面Go源码： //helloworld.go package main import "fmt" func main() { fmt.Println("Hello, World!") } Go支持直接运行某个源文件： $go run helloworld.go Hello, World! 但通常我们会先编译这个源文件(helloworld.go)，生成可执行的二进制程序(./helloworld)，然后再运行它： $go [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/learn-go-in-10-min-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2023/02/23/learn-go-in-10-min">本文永久链接</a> &#8211; https://tonybai.com/2023/02/23/learn-go-in-10-min</p>
<p>本文旨在带大家快速入门Go语言，期望小伙伴们在花费十分钟左右通读全文后能对Go语言有一个初步的认知，为后续进一步深入学习Go奠定基础。</p>
<p>本文假设你完全没有接触过Go，你可能是一名精通其他编程语言的程序员，也可能是毫无编程经验、刚刚想转行为码农的热血青年。</p>
<h2>编程简介</h2>
<p>编程就是<strong>生产可在计算机上执行的程序的过程(如下图)</strong>。在这个过程中，程序员是“劳动力”，编程语言是工具，可执行的程序是生产结果。而Go语言就是程序员在编程生产过程中使用的一种优秀生产工具。</p>
<p><img src="https://tonybai.com/wp-content/uploads/learn-go-in-10-min-2.png" alt="" /></p>
<p>作为“劳动力”的程序员在这个过程中要做的就是使用某种编程语言作为生产工具，将事先设计好的执行逻辑组织和表达出来，这与一个作家将其大脑中设计好的故事情节用人类语言组织和书写在纸上的过程颇为类似(如下图)。</p>
<p><img src="https://tonybai.com/wp-content/uploads/learn-go-in-10-min-3.png" alt="" /></p>
<p>通过这个类比来看，学习一门编程语言，就好比学习一门人类语言，其词汇和语法将是我们的主要学习内容，本文就将围绕Go语言的主要“词汇”和语法形式进行快速说明。</p>
<h2>Go简介</h2>
<p>Go语言是由Google公司的三位大神级程序员Robert Griesemer、Rob Pike和Ken Thompson在2007年共同开发的一种新的后端编程语言，2009年，Go语言宣布开源。</p>
<p>Go语言的特点是简单易学、静态类型、编译速度快，运行效率高，代码简洁，并且原生支持并发编程。它还支持自动内存管理，可以让开发者更加专注于编程本身，而不用担心内存泄漏的问题。此外，Go语言还支持多核处理器，可以更好地利用多核处理器的优势，提高程序的运行效率。</p>
<p><a href="https://tonybai.com/2022/11/11/go-opensource-13-years">经过十多年的发展</a>，Go语言现在已经成为一种流行的编程语言，它可以用于开发各种应用程序，包括Web应用、网络服务、系统管理工具、移动应用、游戏开发、数据库管理等。Go语言常用于构建大型分布式系统，以及构建高性能的服务器端应用程序。Go为当前的云原生计算时代开发了一批“杀手级”应用，包括Docker、Kubernetes、Prometheus、InfluxDB、<a href="https://cilium.io">Cilium</a>等。</p>
<h2>安装Go</h2>
<p>Go是静态语言，需要先编译，再执行，因此在开发Go程序之前，我们首先需要安装Go编译器以及相关工具链。安装的步骤很简单：</p>
<ul>
<li>从<a href="https://go.dev/dl">Go官网下载</a>最新版本的Go语言安装包 &#8211; https://go.dev/dl/</li>
<li>解压安装包，并将其复制到您想要安装的位置，例如：/usr/local/go；如果是Windows、MacOS平台，也可以下载图形化安装的安装包；</li>
<li>设置环境变量，将Go语言的安装路径添加到PATH变量中；</li>
<li>打开终端，输入go version，检查Go语言是否安装成功。如输出类似下面的内容，则表明安装成功！</li>
</ul>
<pre><code>$go version
go version go1.20 darwin/amd64
</code></pre>
<blockquote>
<p>注：位于中国大陆的开发者们还需要一个额外的设置：export GOPROXY=&#8217;https://goproxy.cn&#8217;或将这个设置置于shell配置文件(比如.bashrc)中并使之生效。</p>
</blockquote>
<h2>第一个Go程序：Hello World</h2>
<p>建立一个新目录，并在其中创建新文件helloworld.go，用任意编辑器打开helloworld.go，输入下面Go源码：</p>
<pre><code>//helloworld.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
</code></pre>
<p>Go支持直接运行某个源文件：</p>
<pre><code>$go run helloworld.go
Hello, World!
</code></pre>
<p>但通常我们会先编译这个源文件(helloworld.go)，生成可执行的二进制程序(./helloworld)，然后再运行它：</p>
<pre><code>$go build -o helloworld helloworld.go
$./helloworld
Hello, World!
</code></pre>
<h2>Go包(package)</h2>
<p>Go包是Go语言中的一种封装技术，它可以将一组Go语言源文件组织成一个可重用的单元，以便在其他Go程序中使用。同属于一个Go包的所有源文件放在一个目录下，并且按惯例该目录的名字与包名相同。以Go标准库的io包为例，其包内的源文件列表如下：</p>
<pre><code>// $GOROOT/src/io目录下的文件列表：
io.go
multi.go
pipe.go
</code></pre>
<p>Go包也是Go编译的基本单元，Go编译器可以将包编译为可执行文件(如何该包为main包，且包含main函数实现)，也可以编译为可重用的库文件(.a)。</p>
<h3>包声明</h3>
<p>Go包的声明通常是在每个Go源文件的开头，使用关键字package进行声明，例如：</p>
<pre><code>// mypackage.go
package mypackage

... ...
</code></pre>
<p>package的名字按惯例通常为全小写的单个单词或缩略词，比如io、net、os、fmt、strconv、bytes等。</p>
<h3>导入Go包</h3>
<p>如果要复用已有的Go包，我们需要在源码中导入该包。要导入Go包，可以使用import关键字，例如：</p>
<pre><code>import "fmt"                    // 导入标准库的fmt包

import "github.com/spf13/pflag" // 导入spf13开源的pflag包

import _ "net/http/pprof"       // 导入标准库net/http/pprof包，
                                // 但不显式使用该包中的类型、变量、函数等标识符

import myfmt "fmt"              // 将导入的包重命名为myfmt
</code></pre>
<h2>Go模块</h2>
<p>Go模块(module)是Go语言在<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11">1.11版本</a>中引入的新特性，Go module是一组相关的Go package的集合，这个包集合被当做一个独立的单元进行统一版本管理。Go module这种新的<strong>依赖管理机制</strong>可以让开发者更轻松地管理Go语言项目的依赖关系，并且可以更好地支持多版本的依赖管理。在具有实用价值的Go项目中，我们都会使用Go module进行依赖管理。Go module有版本之分，Go module的版本依赖关系是建立在对<a href="https://semver.org/">语义版本(semver)</a>严格遵守的前提下的。</p>
<p>Go使用go.mod文件来精确记录依赖关系要求，下面是go.mod中依赖关系的操作方法：</p>
<pre><code>$go mod init demo // 创建一个module root为demo的go.mod
$go mod init github.com/bigwhite/mymodule // 创建一个module root为github.com/bigwhite/mymodule的go.mod

$go get github.com/bigwhite/foo@latest  // 向go.mod中添加一个依赖包github.com/bigwhite/foo的最新版本
$go get github.com/bigwhite/foo         // 与上面命令等价
$go get github.com/bigwhite/foo@v1.2.3  // 显式指定要获取v1.2.3版本

$go mod tidy   // 自动添加缺失的依赖包和清理不用的依赖包
$go mod verify // 确认所有依赖都有效
</code></pre>
<h2>Go最小项目结构</h2>
<p>Go官方并没有规定Go项目的标准结构布局，下面是Go核心团队技术负责人Russ Cox推荐的Go最小项目结构：</p>
<pre><code>// 在Go项目仓库根路径下

- go.mod
- LICENSE
- README
- xx.go
- yy.go
... ...
</code></pre>
<p>或</p>
<pre><code>// 在Go项目仓库根路径下

- go.mod
- LICENSE
- README
- package1/
    - package1.go
- package2/
    - package2.go
... ...
</code></pre>
<h2>变量</h2>
<p>Go语言有两种变量声明方式：</p>
<ul>
<li>使用var关键字</li>
</ul>
<p>使用var关键字进行声明的方式适合所有场合。</p>
<pre><code>var a int     // 声明一个int型变量a，初值为0
var b int = 5 // 声明一个int型变量b，初值为5
var c = 6     // Go会根据右值自动为变量c的赋予默认类型，默认的整型为int

var (         // 我们可以将变量声明统一放置在一个var块中，这与上面的声明方式等价
    a int
    b int = 5
    c = 6
)
</code></pre>
<blockquote>
<p>注：Go变量声明采用变量在前，类型在后的方式，这与C、C++、Java等静态编程语言有较大不同。</p>
</blockquote>
<ul>
<li>使用短声明方式声明变量</li>
</ul>
<pre><code>a := 5       // 声明一个变量a，Go会根据右值自动为变量a的赋予默认类型，默认的整型为int
s := "hello" // 声明一个变量s，Go会根据右值自动为变量s的赋予默认类型，默认的字符串类型为string
</code></pre>
<blockquote>
<p>注：这种声明方式仅限于在函数或方法内使用，不能用于声明包级变量或全局变量。</p>
</blockquote>
<h2>常量</h2>
<p>Go语言的常量使用const关键字进行声明：</p>
<pre><code>const a int       // 声明一个int型常量a，其值为0
const b int = 5   // 声明一个int型常量b，其值为5
const c = 6       // 声明一个常量c，Go会根据右值自动为常量c的赋予默认类型，默认的整型为int
const s = "hello" // 声明一个常量s，Go会根据右值自动为常量s的赋予默认类型，默认的字符串类型为string

const (           // 我们可以将常量声明统一放置在一个const块中，这与上面的声明方式等价
    a int
    b int = 5
    c = 6
    s = "hello"
)
</code></pre>
<h2>类型</h2>
<p>Go原生内置了多种基本类型与复合类型。</p>
<h3>基本类型</h3>
<p>Go原生支持的基本类型包括布尔型、数值类型（整型、浮点型、复数类型）、字符串类型，下面是一些示例：</p>
<pre><code>bool  // 布尔类型，默认值false

uint     // 架构相关的无符号整型，64位平台上其长度为8字节
int      // 架构相关的有符号整型，64位平台上其长度为8字节
uintptr  // 架构相关的用于表示指针值的类型，它是一个无符号的整数，大到足以存储一个任意类型的指针的值

uint8    // 架构无关的8位无符号整型
uint16   // 架构无关的16位无符号整型
uint32   // 架构无关的32位无符号整型
uint64   // 架构无关的64位无符号整型

int8     // 架构无关的8位有符号整型
int16    // 架构无关的16位有符号整型
int32    // 架构无关的32位有符号整型
int64    // 架构无关的64位有符号整型

byte     // uint8类型的别名
rune     // int32类型的别名，用于表示一个unicode字符(码点)

float32     // 单精度浮点类型，满足IEEE-754规范
float64     // 双精度浮点类型，满足IEEE-754规范

complex64   // 复数类型，其实部和虚部均为float32浮点类型
complex128  // 复数类型，其实部和虚部均为float64浮点类型

string      // 字符串类型，默认值为""
</code></pre>
<blockquote>
<p>我们可以使用预定义函数complex来构造复数类型，比如：complex(1.0, -1.4)构造的复数为1 &#8211; 1.4i。</p>
</blockquote>
<h3>复合类型</h3>
<p>Go原生支持的复合类型包括数组（array）、切片(slice)、结构体(struct)、指针(pointer)、函数(function)、接口(interface)、map、channel。</p>
<h4>数组类型</h4>
<p><img src="https://tonybai.com/wp-content/uploads/learn-go-in-10-min-4.png" alt="" /></p>
<p>数组类型是一组同构类型元素组成的连续体，它具有固定的长度(length)，不能动态伸缩：</p>
<pre><code>[8]int      // 一个元素类型为int、长度为16的数组类型
[32]byte    // 一个元素类型为byte、长度为32的数组类型
[2]string   // 一个元素类型为string、长度为2的数组类型
[N]T        // 一个元素类型为T、长度为N的数组类型
</code></pre>
<p>通过预定义函数len可以得到数组的长度：</p>
<pre><code>var a = [8]int{11, 12, 13, 14, 15, 16, 17, 18}
println(len(a)) // 8
</code></pre>
<p>通过数组下标(从0开始)可以直接访问到数组中的任意元素：</p>
<pre><code>println(a[0]) // 11
println(a[2]) // 13
println(a[7]) // 18
</code></pre>
<p>Go支持声明多维数组，即数组的元素类型依然为数组类型：</p>
<pre><code>[2][3][5]float64  // 一个多维数组类型，等价于[2]([3]([5]float64))
</code></pre>
<h4>切片类型</h4>
<p><img src="https://tonybai.com/wp-content/uploads/learn-go-in-10-min-5.png" alt="" /></p>
<p>切片类型与数组类型类似，也是同构类型元素的连续体。不同的是切片类型的长度可变，我们在声明切片类型时无需传入长度属性：</p>
<pre><code>[]int       // 一个元素类型为int的切片类型
[]string    // 一个元素类型为string的切片类型
[]T         // 一个元素类型为T的切片类型
[][][]float64 // 多维切片类型，等价于[]([]([]float64))
</code></pre>
<p>通过预定义函数len可以得到切片的当前长度：</p>
<pre><code>var sl = []int{11, 12} // 一个元素类型为int的切片，其长度(len)为2, 其值为[11 12]
println(len(sl)) // 2
</code></pre>
<p>切片还有一个属性，那就是容量，通过预定义函数cap可以获得其容量值：</p>
<pre><code>println(cap(sl)) // 2
</code></pre>
<p>和数组不同，切片可以动态伸缩，Go会根据元素的数量动态对切片容量进行扩展。我们可以通过append函数向切片追加元素：</p>
<pre><code>sl = append(sl, 13)     // 向sl中追加新元素，操作后sl为[11 12 13]
sl = append(sl, 14)     // 向sl中追加新元素，操作后sl为[11 12 13 14]
sl = append(sl, 15)     // 向sl中追加新元素，操作后sl为[11 12 13 14 15]
println(len(sl), cap(sl)) // 5 8 追加后切片容量自动扩展为8
</code></pre>
<p>和数组一样，切片也是使用下标直接访问其中的元素：</p>
<pre><code>println(sl[0]) // 11
println(sl[2]) // 13
println(sl[4]) // 15
</code></pre>
<h4>结构体类型</h4>
<p>Go的结构体类型是一种异构类型字段的聚合体，它提供了一种通用的、对实体对象进行聚合抽象的能力。下面是一个包含三个字段的结构体类型：</p>
<pre><code>struct {
    name string
    age  int
    gender string
}
</code></pre>
<p>我们通常会给这样的一个结构体类型起一个名字，比如下面的Person：</p>
<pre><code>type Person struct {
    name string
    age  int
    gender string
}
</code></pre>
<p>下面声明了一个Person类型的变量：</p>
<pre><code>var p = Person {
    name: "tony bai",
    age: 20,
    gender: "male",
}
</code></pre>
<p>我们可以通过p.FieldName来访问结构体中的字段：</p>
<pre><code>println(p.name) // tony bai
p.age = 21
</code></pre>
<p>结构体类型T的定义中可以包含类型为&#42;T的字段成员，但不能递归包含T类型的字段成员：</p>
<pre><code>type T struct {
    ... ...
    p *T    // ok
    t T     // 错误：递归定义
}
</code></pre>
<p>Go结构体亦可以在定义中嵌入其他类型：</p>
<pre><code>type F struct {
    ... ...
}

type MyInt int

type T struct {
    MyInt
    F
    ... ...
}
</code></pre>
<p>嵌入类型的名字将作为字段名：</p>
<pre><code>var t = T {
    MyInt: 5,
    F: F {
        ... ...
    },
}

println(t.MyInt) // 5
</code></pre>
<p>Go支持不包含任何字段的空结构体：</p>
<pre><code>struct{}
type Empty struct{}        // 一个空结构体类型
</code></pre>
<p>空结构体类型的大小为0，这在很多场景下很有用(省去了内存分配的开销)：</p>
<pre><code>var t = Empty{}
println(unsafe.Sizeof(t)) // 0
</code></pre>
<h4>指针类型</h4>
<p>int类型对应的指针类型为&#42;int，推而广之T类型对应的指针类型为&#42;T。和非指针类型不同，指针类型变量存储的是内存单元的地址，&#42;T指针类型变量的大小与T类型大小无关，而是和系统地址的表示长度有关。</p>
<pre><code>*int     // 一个int指针类型
*[4]byte // 一个[4]byte数组指针类型

var a = 6
var p *T // 声明一个T类型指针变量p，默认值为nil
p = &amp;a   // 用变量a的内存地址给指针变量p赋值
*p = 7   // 指针解引用，通过指针p将变量a的值由6改为7

n := new(int)  // 预定义函数返回一个*int类型指针
arr := new([4]int)  // 使用预定义函数new分配一个[4]int数组并返回一个*[4]int类型指针
</code></pre>
<h4>map类型</h4>
<p>map是Go语言提供的一种抽象数据类型，它表示一组无序的键值对，下面定义了一组map类型：</p>
<pre><code>map[string]int                // 一个key类型为string，value类型为int的map类型
map[*T]struct{ x, y float64 } // 一个key类型为*T，value类型为struct{ x, y float64 }的map类型
map[string]interface{}        // 一个key类型为string，value类型为interface{}的map类型
</code></pre>
<p>我们可以用map字面量或make来创建一个map类型实例：</p>
<pre><code>var m = map[string]int{}      // 声明一个map[string]int类型变量并初始化
var m1 = make(map[string]int) // 与上面的声明等价
var m2 = make(map[string]int, 100) // 声明一个map[string]int类型变量并初始化，其初始容量建议为100
</code></pre>
<p>操作map变量的方法也很简单：</p>
<pre><code>m["key1"] = 5  // 添加/设置一个键值对
v, ok := m["key1"]  // 获取“key1”这个键的值，如果存在，则其值存储在v中，ok为true
delete(m, "key1") // 从m这个map中删除“key1”这个键以及其对应的值
</code></pre>
<h4>其他类型</h4>
<p>函数、接口、channel类型在后面有详细说明。</p>
<h3>自定义类型</h3>
<p>使用type关键字可以实现自定义类型：</p>
<pre><code>type T1 int         // 定义一个新类型T1，其底层类型(underlying type)为int
type T2 string      // 定义一个新类型T2，其底层类型为string
type T3 struct{     // 定义一个新类型T3，其底层类型为一个结构体类型
    x, y int
    z string
}
type T4 []float64   // 定义一个新类型T4，其底层类型为[]float64切片类型
type T5 T4          // 定义一个新类型T5，其底层类型为[]float64切片类型
</code></pre>
<p>Go也支持为类型定义别名(alias)，其形式如下；</p>
<pre><code>type T1 = int       // 定义int的类型别名为T1，T1与int等价
type T2 = string    // 定义string的类型别名为T2，T2与string等价
type T3 = T2        // 定义T的类型别名为T3，T3与T2等价，也与string等价
</code></pre>
<h3>类型转换</h3>
<p>Go不支持隐式自动转型，如果要进行类型转换操作，我们必须显式进行，即便两个类型的底层类型相同也需如此：</p>
<pre><code>type T1 int
type T2 int
var t1 T1
var n int = 5
t1 = T1(n)      // 显式将int类型变量转换为T1类型
var t2 T2
t2 = T2(t1)     // 显式将T1类型变量转换为T2类型
</code></pre>
<p>Go很多原生类型支持相互转换：</p>
<pre><code>// 数值类型的相互转换

var a int16 = 16
b := int32(a)
c := uint16(a)
f := float64(a)

// 切片与数组的转换(Go 1.17版本及后续版本支持)

var a [3]int = [3]int([]int{1,2,3}) // 切片转换为数组
var pa *[3]int = (*[3]int)([]int{1,2,3}) // 切片转换为数组指针
sl := a[:] // 数组转换为切片

// 字符串与切片的相互转换

var sl = []byte{'h', 'e','l', 'l', 'o'}
var s = string(sl) // s为hello
var sl1 = []byte(s) // sl1为['h' 'e' 'l' 'l' 'o']
string([]rune{0x767d, 0x9d6c, 0x7fd4})  // []rune切片到string的转换
</code></pre>
<h2>控制语句</h2>
<p>Go提供了常见的控制语句，包括条件分支(if)、循环语句(for)和选择分支语句(switch)。</p>
<h3>条件分支语句</h3>
<pre><code>// if ...

if a == 1 {
    ... ...
}

// if - else if - else

if a == 1 {

} else if b == 2 {

} else {

}

// 带有条件语句自用变量
if a := 1; a != 0 {

}

// if语句嵌套

if a == 1 {
    if b == 2 {

    } else if c == 3 {

    } else {

    }
}
</code></pre>
<h3>循环语句</h3>
<pre><code>// 经典循环

for i := 0; i &lt; 10; i++ {
    ...
}

// 模拟while ... do

for i &lt; 10 {

}

// 无限循环

for {

}

// for range

var s = "hello"
for i, c := range s {

}

var sl = []int{... ...}
for i, v := range sl {

}

var m = map[string]int{}
for k, v := range m {

}

var c = make(chan int, 100)
for v := range c {

}
</code></pre>
<h3>选择分支语句</h3>
<pre><code>var n = 5
switch n {
    case 0, 1, 2, 3:
        s1()
    case 4, 5, 6, 7:
        s2()
    default: // 默认分支
        s3()
}

switch n {
    case 0, 1:
        fallthrough  // 显式告知执行下面分支的动作
    case 2, 3:
        s1()
    case 4, 5, 6, 7:
        s2()
    default:
        s3()
}

switch x := f(); {
    case x &lt; 0:
        return -x
    default:
        return x
}

switch {
    case x &lt; y:
        f1()
    case x &lt; z:
        f2()
    case x == 4:
        f3()
}
</code></pre>
<h2>函数</h2>
<p>Go使用func关键字来声明一个函数：</p>
<pre><code>func greet(name string) string {
    return fmt.Sprintf("Hello %s", name)
}
</code></pre>
<p>函数由函数名、可选的参数列表和返回值列表组成。Go函数支持返回多个返回值，并且我们通常将表示错误值的返回类型放在返回值列表的最后面：</p>
<pre><code>func Atoi(s string) (int, error) {
    ... ...
    return n, nil
}
</code></pre>
<p>在Go中函数是一等公民，因此函数自身也可以作为参数或返回值：</p>
<pre><code>func MultiplyN(n int) func(x int) int {
  return func(x int) int {
    return x * n
  }
}
</code></pre>
<p>像上面MultiplyN函数中定义的匿名函数func(x int) int，它的实现中引用了它的外围函数MultiplyN的参数n，这样的匿名函数也被称为<strong>闭包(closure)</strong>。</p>
<p>说到函数，我们就不能不提defer。在某函数F调用的前面加上defer，该函数F的执行将被“延后”至其调用者A结束之后：</p>
<pre><code>func F() {
    fmt.Println("call F")
}

func A() {
    fmt.Println("call A")
    defer F()
    fmt.Println("exit A")
}

func main() {
    A()
}
</code></pre>
<p>上面示例输出：</p>
<pre><code>call A
exit A
call F
</code></pre>
<p>在一个函数中可以多次使用defer：</p>
<pre><code>func B() {
    defer F()
    defer G()
    defer H()
}
</code></pre>
<p>被defer修饰的函数将按照“先入后出”的顺序在B函数结束后被调用，上面B函数执行后将输出：</p>
<pre><code>call H
call G
call F
</code></pre>
<h2>方法</h2>
<p>方法是带有receiver的函数。下面是Point类型的一个方法Length：</p>
<pre><code>type Point struct {
    x, y float64
}

func (p Point) Length() float64 {
    return math.Sqrt(p.x * p.x + p.y * p.y)
}
</code></pre>
<p>而在func关键字与函数名之间的部分便是receiver。这个receiver也是Length方法与Point类型之间纽带。我们可以通过Point类型变量来调用Length方法：</p>
<pre><code>var p = Point{3,4}
fmt.Println(p.Length())
</code></pre>
<p>亦可以将方法当作函数来用：</p>
<pre><code>var p = Point{3,4}
fmt.Println(Point.Length(p)) // 这种用法也被称为方法表达式(method expression)
</code></pre>
<h2>接口</h2>
<p>接口是一组方法的集合，它代表一个“契约”，下面是一个由三个方法组成的方法集合的接口类型：</p>
<pre><code>type MyInterface interface {
    M1(int) int
    M2(string) error
    M3()
}
</code></pre>
<p>Go推崇<strong>面向接口编程</strong>，因为通过接口我们可以很容易构建<strong>低耦合</strong>的应用。</p>
<p>Go还支持在接口类型(如I)中嵌套其他接口类型(如io.Writer、sync.Locker)，其结果就是新接口类型I的方法集合为其方法集合与嵌入的接口类型Writer和Locker的方法集合的并集：</p>
<pre><code>type I interface { // 一个嵌入了其他接口类型的接口类型
   io.Writer
   sync.Locker
}
</code></pre>
<h3>接口实现</h3>
<p>如果一个类型T实现了某个接口类型MyInterface方法集合中的所有方法，那么我们说该类型T实现了接口MyInterface，于是T类型的变量t可以赋值给接口类型MyInterface的变量i，此时变量i的<strong>动态类型</strong>为T：</p>
<pre><code>var t T
var i MyInterface = t // ok
</code></pre>
<p>通过上述变量i可以调用T的方法：</p>
<pre><code>i.M1(5)
i.M2("demo")
i.M3()
</code></pre>
<p>方法集合为空的接口类型interface{}被称为“空接口类型”，空白的“契约”意味着任何类型都实现了该空接口类型，即任何变量都可以赋值给interface{}类型的变量：</p>
<pre><code>var i interface{} = 5 // ok
i = "demo"            // ok
i = T{}               // ok
i = &amp;T{}              // ok
i = []T{}             // ok
</code></pre>
<blockquote>
<p>注：Go 1.18中引入的新预定义标识符any与interface{}是等价类型。</p>
</blockquote>
<h3>接口的类型断言</h3>
<p>Go支持通过类型断言从接口变量中提取其动态类型的值：</p>
<pre><code>v, ok := i.(T) // 类型断言
</code></pre>
<p>如果接口变量i的动态类型确为T，那么v将被赋予该动态类型的值，ok为true；否则，v为T类型的零值，ok为false。</p>
<p>类型断言也支持下面这种语法形式：</p>
<pre><code>v := i.(T)
</code></pre>
<p>但在这种形式下，一旦接口变量i之前被赋予的值不是T类型的值，那么这个语句将抛出panic。</p>
<h3>接口类型的type switch</h3>
<p>“type switch”这是一种特殊的switch语句用法，仅用于接口类型变量：</p>
<pre><code>func main() {
    var x interface{} = 13
    switch x.(type) {
    case nil:
        println("x is nil")
    case int:
        println("the type of x is int") // 执行这一分支case
    case string:
        println("the type of x is string")
    case bool:
        println("the type of x is string")
    default:
        println("don't support the type")
    }
}
</code></pre>
<p>switch关键字后面跟着的表达式为x.(type)，这种表达式形式是switch语句专有的，而且也只能在switch语句中使用。这个表达式中的x必须是一个接口类型变量，表达式的求值结果是这个接口类型变量对应的动态类型。</p>
<p>上述例子中switch后面的表达式也可由x.(type)换成了v := x.(type)。v中将存储变量x的动态类型对应的值信息：</p>
<pre><code>var x interface{} = 13
switch x.(type) {
    case nil:
        println("v is nil")
    case int:
        println("the type of v is int, v =", v) // 执行这一分支case，v = 13
    ... ...
}
</code></pre>
<h2>泛型</h2>
<p><a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go从1.18版本开始支持泛型</a>。Go泛型的基本语法是类型参数(type parameter)，Go泛型方案的实质是对类型参数的支持，包括：</p>
<ul>
<li>泛型函数（generic function）：带有类型参数的函数；</li>
<li>泛型类型（generic type）：带有类型参数的自定义类型；</li>
<li>泛型方法（generic method）：泛型类型的方法。</li>
</ul>
<h3>泛型函数</h3>
<p>下面是一个泛型函数max的定义：</p>
<pre><code>type ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
        ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
        ~float32 | ~float64 |
        ~string
}

func max[T ordered](sl []T) T {
    ... ...
}
</code></pre>
<p>与普通Go函数相比，max函数在函数名称与函数参数列表之间多了一段由方括号括起的代码：[T ordered]；max参数列表中的参数类型以及返回值列表中的返回值类型都是T，而不是某个具体的类型。</p>
<p>max函数中多出的[T ordered]就是Go泛型的类型参数列表（type parameters list），示例中这个列表中仅有一个类型参数T，ordered为类型参数的类型约束（type constraint）。</p>
<p>我们可以像普通函数一样调用泛型函数，我们可以显式指定类型实参：</p>
<pre><code>var m int = max[int]([]int{1, 2, -4, -6, 7, 0})  // 显式指定类型实参为int
fmt.Println(m) // 输出：7
</code></pre>
<p>Go也支持自动推断出类型实参：</p>
<pre><code>var m int = max([]int{1, 2, -4, -6, 7, 0}) // 自动推断T为int
fmt.Println(m) // 输出：7
</code></pre>
<h3>泛型类型</h3>
<p>所谓泛型类型，就是在类型声明中带有类型参数的Go类型：</p>
<pre><code>type Set[T comparable] map[T]string

type element[T any] struct {
    next *element[T]
    val  T
}

type Map[K, V any] struct {
  root    *node[K, V]
  compare func(K, K) int
}
</code></pre>
<p>以泛型类型Set为例，其使用方法如下：</p>
<pre><code>var s = Set[string]{}
s["key1"] = "value1"
println(s["key1"]) // value1
</code></pre>
<h3>泛型方法</h3>
<p>Go类型可以拥有自己的方法（method），泛型类型也不例外，为泛型类型定义的方法称为泛型方法（generic method）。</p>
<pre><code>type Set[T comparable] map[T]string

func (s Set[T]) Insert(key T, val string) {
    s[key] = val
}

func (s Set[T]) Get(key T) (string, error) {
    val, ok := s[key]
    if !ok {
        return "", errors.New("not found")
    }
    return val, nil
}

func main() {
    var s = Set[string]{
        "key": "value1",
    }
    s.Insert("key2", "value2")
    v, err := s.Get("key2")
    fmt.Println(v, err) // value2 &lt;nil&gt;
}
</code></pre>
<h3>类型约束</h3>
<p>Go通过类型约束(constraint)对泛型函数的类型参数以及泛型函数中的实现代码设置限制。Go使用扩展语法后的interface类型来定义约束。</p>
<p>下面是使用常规接口类型作为约束的例子：</p>
<pre><code>type Stringer interface {
    String() string
}

func Stringify[T fmt.Stringer](s []T) (ret []string) { // 通过Stringer约束了T的实参只能是实现了Stringer接口的类型
    for _, v := range s {
        ret = append(ret, v.String())
    }
    return ret
}
</code></pre>
<p>Go接口类型声明语法做了扩展，支持在接口类型中放入类型元素（type element）信息：</p>
<pre><code>type ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
        ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
        ~float32 | ~float64 | ~string
}

func Less[T ordered](a, b T) bool {
    return a &lt; b
}

type Person struct {
    name string
    age  int
}

func main() {
    println(Less(1, 2)) // true
    println(Less(Person{"tony", 11}, Person{"tom", 23})) // Person不满足ordered的约束，会导致编译错误
}
</code></pre>
<h2>并发</h2>
<p>Go语言原生支持并发，Go并没有使用操作系统线程作为并发的基本执行单元，而是实现了goroutine这一由Go运行时（runtime）负责调度的、轻量的用户级线程，为并发程序设计提供原生支持。</p>
<h3>goroutine</h3>
<p>通过go关键字+函数/方法的方式，我们便可以创建一个goroutine。创建后，新goroutine将拥有独立的代码执行流，并与创建它的goroutine一起被Go运行时调度。</p>
<pre><code>go fmt.Println("I am a goroutine")

// $GOROOT/src/net/http/server.go
c := srv.newConn(rw)
go c.serve(connCtx)
</code></pre>
<p>goroutine的执行函数返回后，goroutine便退出。如果是主goroutine(执行main.main的goroutine)退出，那么整个Go应用进程将会退出，程序生命周期结束。</p>
<h3>channel</h3>
<p>Go提供了原生的用于goroutine之间通信的机制channel，channel的定义与操作方式如下：</p>
<pre><code>// channel类型
chan T          // 一个元素类型为T的channel类型
chan&lt;- float64  // 一个元素类型为float64的只发送channel类型
&lt;-chan int      // 一个元素类型为int的只接收channel类型

var c chan int             // 声明一个元素类型为int的channel类型的变量，初值为nil
c1 := make(chan int)       // 声明一个元素类型为int的无缓冲的channel类型的变量
c2 := make(chan int, 100)  // 声明一个元素类型为int的带缓冲的channel类型的变量，缓冲大小为100
close(c)                   // 关闭一个channel
</code></pre>
<p>下面是两个goroutine基于channel通信的例子：</p>
<pre><code>func main() {
    var c = make(chan int)
    go func(a, b int) {
        c &lt;- a + b
    }(3,4)
    println(&lt;-c) // 7
}
</code></pre>
<p>当涉及同时对多个channel进行操作时，Go提供了select机制。通过select，我们可以同时在多个channel上进行发送/接收操作：</p>
<pre><code>select {
case x := &lt;-ch1:     // 从channel ch1接收数据
  ... ...

case y, ok := &lt;-ch2: // 从channel ch2接收数据，并根据ok值判断ch2是否已经关闭
  ... ...

case ch3 &lt;- z:       // 将z值发送到channel ch3中:
  ... ...

default:             // 当上面case中的channel通信均无法实施时，执行该默认分支
}
</code></pre>
<h2>错误处理</h2>
<p>Go提供了简单的、基于错误值比较的错误处理机制，这种机制让每个开发人员必须显式地去关注和处理每个错误。</p>
<h3>error类型</h3>
<p>Go用error这个接口类型表示错误，并且按惯例，我们通常将error类型返回值放在返回值列表的末尾。</p>
<pre><code>// $GOROOT/src/builtin/builtin.go
type error interface {
    Error() string
}
</code></pre>
<p>任何实现了error的Error方法的类型的实例，都可以作为错误值赋值给error接口变量。</p>
<p>Go提供了便捷的构造错误值的方法：</p>
<pre><code>err := errors.New("your first demo error")
errWithCtx = fmt.Errorf("index %d is out of bounds", i)
</code></pre>
<h3>错误处理形式</h3>
<p>Go最常见的错误处理形式如下：</p>
<pre><code>err := doSomething()
if err != nil {
    ... ...
    return err
}
</code></pre>
<p>通常我们会定义一些“哨兵”错误值来辅助错误处理方检视（inspect）错误值并做出错误处理分支的决策：</p>
<pre><code>// $GOROOT/src/bufio/bufio.go
var (
    ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
    ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
    ErrBufferFull        = errors.New("bufio: buffer full")
    ErrNegativeCount     = errors.New("bufio: negative count")
)

func doSomething() {
    ... ...
    data, err := b.Peek(1)
    if err != nil {
        switch err {
        case bufio.ErrNegativeCount:
            // ... ...
            return
        case bufio.ErrBufferFull:
            // ... ...
            return
        case bufio.ErrInvalidUnreadByte:
            // ... ...
            return
        default:
            // ... ...
            return
        }
    }
    ... ...
}
</code></pre>
<h3>Is和As</h3>
<p>从Go 1.13版本开始，标准库errors包提供了Is函数用于错误处理方对错误值的检视。Is函数类似于把一个error类型变量与“哨兵”错误值进行比较：</p>
<pre><code>// 类似 if err == ErrOutOfBounds{ … }
if errors.Is(err, ErrOutOfBounds) {
    // 越界的错误处理
}
</code></pre>
<p>不同的是，如果error类型变量的底层错误值是一个包装错误（Wrapped Error），errors.Is方法会沿着该包装错误所在错误链（Error Chain)，与链上所有被包装的错误（Wrapped Error）进行比较，直至找到一个匹配的错误为止。</p>
<p>标准库errors包还提供了As函数给错误处理方检视错误值。As函数类似于通过类型断言判断一个error类型变量是否为特定的自定义错误类型：</p>
<pre><code>// 类似 if e, ok := err.(*MyError); ok { … }
var e *MyError
if errors.As(err, &amp;e) {
    // 如果err类型为*MyError，变量e将被设置为对应的错误值
}
</code></pre>
<p>如果error类型变量的动态错误值是一个包装错误，errors.As函数会沿着该包装错误所在错误链，与链上所有被包装的错误的类型进行比较，直至找到一个匹配的错误类型，就像errors.Is函数那样。</p>
<h2>小结</h2>
<p>读到这里，你已经对Go语言有了入门级的认知，但要想成为一名Gopher(对Go开发人员的称呼)，还需要更进一步的学习与实践。我的极客时间专栏<a href="http://gk.link/a/10AVZ">《Go语言第一课》</a>是一个很好的起点，欢迎大家订阅学习^_^。</p>
<p>BTW，本文部分内容由<a href="https://chat.openai.com/">ChatGPT</a>生成！你能猜到是哪些部分吗^_^。</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2023年，Gopher部落将进一步聚焦于如何编写雅、地道、可读、可测试的Go代码，关注代码质量并深入理解Go核心技术，并继续加强与星友的互动。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博(暂不可用)：https://weibo.com/bigwhite20xx</li>
<li>微博2：https://weibo.com/u/6484441286</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2023, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2023/02/23/learn-go-in-10-min/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Go程序员拥抱C语言简明指南</title>
		<link>https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher/</link>
		<comments>https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher/#comments</comments>
		<pubDate>Sun, 15 May 2022 23:11:16 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ANSI-C]]></category>
		<category><![CDATA[archive]]></category>
		<category><![CDATA[break]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[C11]]></category>
		<category><![CDATA[C18]]></category>
		<category><![CDATA[C99]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[Clang]]></category>
		<category><![CDATA[clang-format]]></category>
		<category><![CDATA[CMake]]></category>
		<category><![CDATA[Configure]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[C标准库]]></category>
		<category><![CDATA[C语言]]></category>
		<category><![CDATA[DennisRitchie]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[fallthrough]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[Go语言第一课]]></category>
		<category><![CDATA[iso]]></category>
		<category><![CDATA[K&R]]></category>
		<category><![CDATA[KenThompson]]></category>
		<category><![CDATA[LeetCode]]></category>
		<category><![CDATA[libc]]></category>
		<category><![CDATA[Lint]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[loccount]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[Make]]></category>
		<category><![CDATA[Makefile]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[soname]]></category>
		<category><![CDATA[switch-case]]></category>
		<category><![CDATA[TIOBE]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[utf-8]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[函数]]></category>
		<category><![CDATA[动态共享库]]></category>
		<category><![CDATA[命令式编程]]></category>
		<category><![CDATA[国际标准化组织和国际电工委员会]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[增量构建]]></category>
		<category><![CDATA[宏]]></category>
		<category><![CDATA[构建]]></category>
		<category><![CDATA[目标文件]]></category>
		<category><![CDATA[美国国家标准学会]]></category>
		<category><![CDATA[设计哲学]]></category>
		<category><![CDATA[链接器]]></category>
		<category><![CDATA[错误处理]]></category>
		<category><![CDATA[静态语言]]></category>
		<category><![CDATA[预处理]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3535</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher 本文是为于航老师的极客时间专栏《深入C语言和程序运行原理》写的加餐文章《Tony Bai：Go程序员拥抱C语言简明指南》，这里分享给大家，尤其是那些想学习C语言的Gopher们。 你好，我是Tony Bai。 也许有同学对我比较熟悉，看过我在极客时间上的专栏《Tony Bai ·Go语言第一课》，或者是关注了我的博客。那么，作为一个Gopher，我怎么跑到这个C语言专栏做分享了呢？其实，在学习Go语言并成为一名Go程序员之前，我也曾是一名地地道道的C语言程序员。 大学毕业后，我就开始从事C语言后端服务开发工作，在电信增值领域摸爬滚打了十多年。不信的话，你可以去翻翻我的博客，数一数我发的C语言相关文章是不是比关于Go的还多。一直到近几年，我才将工作中的主力语言从C切换到了Go。不过这并不是C语言的问题，主要原因是我转换赛道了。我目前在智能网联汽车领域从事面向云原生平台的先行研发，而在云原生方面，新生代的Go语言有着更好的生态。 不过作为资深C程序员，C语言已经在我身上打下了深深的烙印。虽然Go是我现在工作中的主力语言，但我仍然会每天阅读一些C开源项目的源码，每周还会写下数百行的C代码。在一些工作场景中，特别是在我参与先行研发一些车端中间件时，C语言有着资源占用小、性能高的优势，这一点是Go目前还无法匹敌的。 正因为我有着C程序员和Go程序员的双重身份，接到这个加餐邀请时，我就想到了一个很适合聊的话题——在 Gopher（泛指Go程序员）与C语言之间“牵线搭桥”。在这门课的评论区里，我看到一些同学说，“正是因为学了Go，所以我想学好C”。如果你也对Go比较熟悉，那么恭喜你，这篇加餐简直是为你量身定制的：一个熟悉Go的程序员在学习C时需要注意的问题，还有可能会遇到的坑，我都替你总结好了。 当然，我知道还有一些对Go了解不多的同学，看到这里也别急着退出去。因为C和Go这两门语言的比较，本身就是一个很有意思的话题。今天的加餐，会涉及这两门语言的异同点，通过对C与Go语言特性的比较，你就能更好地理解“C 语言为什么设计成现在这样”。 一. C语言是现代IT工业的根基 在比较C和Go之前，先说说我推荐Gopher学C的最重要原因吧：用一句话总结，C语言在IT工业中的根基地位，是Go和其他语言目前都无法动摇的。 C语言是由美国贝尔实验室的丹尼斯·里奇（Dennis Ritchie）以Unix发明人肯·汤普森（Ken Thompson）设计的B语言为基础而创建的高级编程语言。诞生于上个世纪（精确来说是1972年）的它，到今年（2022年）已到了“知天命”的半百年纪。 年纪大、设计久远一直是“C语言过时论”兴起的根源，但如果你相信这一论断，那就大错特错了。下面，我来为你分析下个中缘由。 首先，我们说说C语言本身：C语言一直在演进，从未停下过脚步。 虽然C语言之父丹尼斯·里奇不幸于2011年永远地离开了我们，但C语言早已成为ANSI（美国国家标准学会）标准以及ISO/IEC（国际标准化组织和国际电工委员会）标准，因此其演进也早已由标准委员会负责。我们来简单回顾一下C语言标准的演进过程： 1989年，ANSI发布了首个C语言标准，被称为C89，又称ANSI C。次年，ISO和IEC把ANSI C89标准定为C语言的国际标准（ISO/IEC 9899:1990），又称C90，它也是C语言的第一个官方版本； 1999年，ISO和IEC发布了C99标准(ISO/IEC 9899:1999)，它是C语言的第二个官方版本； 2011年，ISO和IEC发布了C11标准(ISO/IEC 9899:2011)，它是C语言的第三个官方版本； 2018年，ISO和IEC发布了C18标准(ISO/IEC 9899:2018)，它是C语言的第四个官方版本。 目前，ISO/IEC标准化委员会正在致力于C2x标准的改进与制定，预计它会在2023年发布。 其次，时至今日，C语言的流行度仍然非常高。 著名编程语言排行榜TIOBE的数据显示，各大编程语言年度平均排名的总位次，C语言多年来高居第一，如下图（图片来自TIOBE）所示： 这说明，无论是在过去还是现在，C语言都是一门被广泛应用的工业级编程语言。 最后，也是最重要的一点是：C语言是现代IT工业的根基，我们说C永远不会退出IT行业舞台也不为过。 如今，无论是普通消费者端的Windows、macOS、Android、苹果iOS，还是服务器端的Linux、Unix等操作系统，亦或是各个工业嵌入式领域的操作系统，其内核实现语言都是C语言。互联网时代所使用的主流Web服务器，比如 Nginx、Apache，以及主流数据库，比如MySQL、Oracle、PostgreSQL等，也都是使用C语言开发的杰作。可以说，现代人类每天都在跟由C语言实现的系统亲密接触，并且已经离不开这些系统了。回到我们程序员的日常，Git、SVN等我们时刻在用的源码版本控制软件也都是由C语言实现的。 可以说，C语言在IT工业中的根基地位，不光Go语言替代不了，C++、Rust等系统编程语言也无法动摇，而且不仅短期如此，长期来看也是如此。 总之，C语言具有紧凑、高效、移植性好、对内存的精细控制等优秀特性，这使得我们在任何时候学习它都不会过时。不过，我在这里推荐Gopher去了解和系统学习C语言，其实还有另一个原因。我们继续往下看。 二. C与Go的相通之处：Gopher拥抱C语言的“先天优势” 众所周知，Go 是在C语言的基础上衍生而来的，二者之间有很多相通之处，因此 Gopher 在学习C语言时是有“先天优势”的。接下来，我们具体看看C和Go的相通之处有哪些。 1. 简单且语法同源 Go语言以简单著称，而作为Go先祖的C语言，入门门槛同样不高：Go有25个关键字，C有32个关键字（C89标准），简洁程度在伯仲之间。C语言曾长期作为高校计算机编程教育的首选编程语言，这与C的简单也不无关系。 和Go不同的是，C语言是一个小内核、大外延的编程语言，其简单主要体现在小内核上了。这个“小内核”包括C基本语法与其标准库，我们可以快速掌握它。但需要注意的是，与Go语言“开箱即用、内容丰富”的标准库不同，C标准库非常小（在C11标准之前甚至连thread库都不包含），所以掌握“小内核”后，在LeetCode平台上刷题是没有任何问题的，但要写出某一领域的工业级生产程序，我们还有很多外延知识技能要学习，比如并发原语、操作系统的系统调用，以及进程间通信等。 C语言的这种简单很容易获得Gopher们的认同感。当年Go语言之父们在设计Go语言时，也是主要借鉴了C语言的语法。当然，这与他们深厚的C语言背景不无关系：肯·汤普森（Ken [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/the-short-guide-of-embracing-c-lang-for-gopher-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher">本文永久链接</a> &#8211; https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher</p>
<p>本文是为于航老师的极客时间专栏<a href="http://gk.link/a/11osT">《深入C语言和程序运行原理》</a>写的加餐文章<a href="https://time.geekbang.org/column/article/500145">《Tony Bai：Go程序员拥抱C语言简明指南》</a>，这里分享给大家，尤其是那些想学习C语言的Gopher们。</p>
<hr />
<p>你好，我是Tony Bai。</p>
<p>也许有同学对我比较熟悉，看过我在极客时间上的专栏<a href="http://gk.link/a/10AVZ">《Tony Bai ·Go语言第一课》</a>，或者是关注了<a href="https://tonybai.com">我的博客</a>。那么，作为一个Gopher，我怎么跑到这个C语言专栏做分享了呢？其实，在学习Go语言并成为一名Go程序员之前，我也曾是一名地地道道的C语言程序员。</p>
<p>大学毕业后，我就开始从事C语言后端服务开发工作，在电信增值领域摸爬滚打了十多年。不信的话，你可以去翻翻<a href="https://tonybai.com/tag/c">我的博客</a>，数一数我发的C语言相关文章是不是比关于Go的还多。一直到近几年，我才将工作中的主力语言从C切换到了Go。不过这并不是C语言的问题，主要原因是我转换赛道了。我目前在智能网联汽车领域从事面向云原生平台的先行研发，而在云原生方面，新生代的Go语言有着更好的生态。</p>
<p>不过作为资深C程序员，C语言已经在我身上打下了深深的烙印。虽然Go是我现在工作中的主力语言，但我仍然会每天阅读一些C开源项目的源码，每周还会写下数百行的C代码。在一些工作场景中，特别是在我参与先行研发一些车端中间件时，C语言有着资源占用小、性能高的优势，这一点是Go目前还无法匹敌的。</p>
<p>正因为我有着C程序员和Go程序员的双重身份，接到这个加餐邀请时，我就想到了一个很适合聊的话题——在 Gopher（泛指Go程序员）与C语言之间“牵线搭桥”。在这门课的评论区里，我看到一些同学说，“正是因为学了Go，所以我想学好C”。如果你也对Go比较熟悉，那么恭喜你，这篇加餐简直是为你量身定制的：一个熟悉Go的程序员在学习C时需要注意的问题，还有可能会遇到的坑，我都替你总结好了。</p>
<p><strong>当然，我知道还有一些对Go了解不多的同学，看到这里也别急着退出去。</strong>因为C和Go这两门语言的比较，本身就是一个很有意思的话题。今天的加餐，会涉及这两门语言的异同点，通过对C与Go语言特性的比较，你就能更好地理解“C 语言为什么设计成现在这样”。</p>
<h2>一. C语言是现代IT工业的根基</h2>
<p>在比较C和Go之前，先说说我推荐Gopher学C的最重要原因吧：用一句话总结，<strong>C语言在IT工业中的根基地位，是Go和其他语言目前都无法动摇的</strong>。</p>
<p>C语言是由美国贝尔实验室的丹尼斯·里奇（Dennis Ritchie）以Unix发明人肯·汤普森（Ken Thompson）设计的B语言为基础而创建的高级编程语言。诞生于上个世纪（精确来说是1972年）的它，到今年（2022年）已到了“知天命”的半百年纪。 年纪大、设计久远一直是“C语言过时论”兴起的根源，但如果你相信这一论断，那就大错特错了。下面，我来为你分析下个中缘由。</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-short-guide-of-embracing-c-lang-for-gopher-3.jpeg" alt="" /></p>
<p>首先，我们说说C语言本身：<strong>C语言一直在演进，从未停下过脚步</strong>。</p>
<p>虽然C语言之父丹尼斯·里奇不幸于2011年永远地离开了我们，但C语言早已成为ANSI（美国国家标准学会）标准以及ISO/IEC（国际标准化组织和国际电工委员会）标准，因此其演进也早已由标准委员会负责。我们来简单回顾一下C语言标准的演进过程：</p>
<ul>
<li>1989年，ANSI发布了首个C语言标准，被称为C89，又称ANSI C。次年，ISO和IEC把ANSI C89标准定为C语言的国际标准（ISO/IEC 9899:1990），又称C90，它也是C语言的第一个官方版本；</li>
<li>1999年，ISO和IEC发布了<a href="https://www.iso.org/standard/29237.html">C99标准(ISO/IEC 9899:1999)</a>，它是C语言的第二个官方版本；</li>
<li>2011年，ISO和IEC发布了<a href="https://www.iso.org/standard/57853.html">C11标准(ISO/IEC 9899:2011)</a>，它是C语言的第三个官方版本；</li>
<li>2018年，ISO和IEC发布了<a href="https://www.iso.org/standard/74528.html">C18标准(ISO/IEC 9899:2018)</a>，它是C语言的第四个官方版本。<br />
目前，ISO/IEC标准化委员会正在致力于C2x标准的改进与制定，预计它会在2023年发布。</li>
</ul>
<p>其次，<strong>时至今日，C语言的流行度仍然非常高</strong>。</p>
<p>著名编程语言排行榜TIOBE的数据显示，各大编程语言年度平均排名的总位次，C语言多年来高居第一，如下图（图片来自<a href="https://www.tiobe.com/tiobe-index">TIOBE</a>）所示：</p>
<p><img src="https://tonybai.com/wp-content/uploads/the-short-guide-of-embracing-c-lang-for-gopher-2.png" alt="" /></p>
<p>这说明，无论是在过去还是现在，C语言都是一门被广泛应用的工业级编程语言。</p>
<p>最后，也是最重要的一点是：<strong>C语言是现代IT工业的根基</strong>，我们说C永远不会退出IT行业舞台也不为过。</p>
<p>如今，无论是普通消费者端的Windows、macOS、Android、苹果iOS，还是服务器端的Linux、Unix等操作系统，亦或是各个工业嵌入式领域的操作系统，其内核实现语言都是C语言。互联网时代所使用的主流Web服务器，比如 Nginx、Apache，以及主流数据库，比如MySQL、Oracle、PostgreSQL等，也都是使用C语言开发的杰作。可以说，现代人类每天都在跟由C语言实现的系统亲密接触，并且已经离不开这些系统了。回到我们程序员的日常，Git、SVN等我们时刻在用的源码版本控制软件也都是由C语言实现的。</p>
<p>可以说，C语言在IT工业中的根基地位，不光Go语言替代不了，C++、Rust等系统编程语言也无法动摇，而且不仅短期如此，长期来看也是如此。</p>
<p>总之，C语言具有紧凑、高效、移植性好、对内存的精细控制等优秀特性，这使得我们在任何时候学习它都不会过时。不过，我在这里推荐Gopher去了解和系统学习C语言，其实还有另一个原因。我们继续往下看。</p>
<h2>二. C与Go的相通之处：Gopher拥抱C语言的“先天优势”</h2>
<p>众所周知，Go 是在C语言的基础上衍生而来的，二者之间有很多相通之处，因此 Gopher 在学习C语言时是有“先天优势”的。接下来，我们具体看看C和Go的相通之处有哪些。</p>
<h3>1. 简单且语法同源</h3>
<p>Go语言以简单著称，而作为<strong>Go先祖</strong>的C语言，入门门槛同样不高：Go有25个关键字，C有32个关键字（C89标准），简洁程度在伯仲之间。C语言曾长期作为高校计算机编程教育的首选编程语言，这与C的简单也不无关系。</p>
<p>和Go不同的是，C语言是一个<strong>小内核、大外延</strong>的编程语言，其简单主要体现在小内核上了。这个“小内核”包括C基本语法与其标准库，我们可以快速掌握它。但需要注意的是，与Go语言“开箱即用、内容丰富”的标准库不同，<a href="https://en.wikipedia.org/wiki/C_standard_library">C标准库</a>非常小（在C11标准之前甚至连thread库都不包含），所以掌握“小内核”后，在LeetCode平台上刷题是没有任何问题的，但要写出某一领域的工业级生产程序，我们还有很多外延知识技能要学习，比如并发原语、操作系统的系统调用，以及进程间通信等。</p>
<p>C语言的这种简单很容易获得Gopher们的认同感。当年Go语言之父们在设计Go语言时，也是主要借鉴了C语言的语法。当然，这与他们深厚的C语言背景不无关系：肯·汤普森（Ken Thompson）是Unix之父，与丹尼斯·里奇共同设计了C语言；罗博·派克（Rob Pike）是贝尔实验室的资深研究员，参与了Unix系统的演进、Plan9操作系统的开发，还是UTF-8编码的发明人；罗伯特·格瑞史莫（Robert Griesemer）也是用C语言手写Java虚拟机的大神级人物。</p>
<p>Go的第一版编译器就是由肯·汤普森（Ken Thompson）用C语言实现的。并且，Go语言的早期版本中，C代码的比例还不小。以Go语言发布的第一个版本，<a href="https://github.com/golang/go/releases/tag/go1">Go 1.0版本</a>为例，我们通过<a href="https://gitlab.com/esr/loccount">loccount工具</a>对其进行分析，会得到下面的结果：</p>
<pre><code>$loccount .
all          SLOC=460992  (100.00%) LLOC=193045  in 2746 files
Go           SLOC=256321  (55.60%)  LLOC=109763  in 1983 files
C            SLOC=148001  (32.10%)  LLOC=73458   in 368 files
HTML         SLOC=25080   (5.44%)   LLOC=0       in 57 files
asm          SLOC=10109   (2.19%)   LLOC=0       in 133 files
... ...
</code></pre>
<p>这里我们看到，在1.0版本中，C语言代码行数占据了32.10%的份额，这一份额直至Go 1.5版本实现自举后，才下降为不到1%。</p>
<p>我当初对Go“一见钟情”，其中一个主要原因就是Go与C语言的<strong>语法同源。</strong>相对应地，相信这种同源的语法也会让Gopher们喜欢上C语言。</p>
<h3>2. 静态编译且基础范式相同</h3>
<p>除了语法同源，C语言与Go语言的另一个相同点是，它们都是静态编译型语言。这意味着它们都有如下的语法特性：</p>
<ul>
<li>变量与函数都要先声明后才能使用；</li>
<li>所有分配的内存块都要有对应的类型信息，并且在确定其类型信息后才能操作；</li>
<li>源码需要先编译链接后才能运行。</li>
</ul>
<p>相似的编程逻辑与构建过程，让学习C语言的Gopher可以做到无缝衔接。</p>
<p>除此之外，Go 和C的基础编程范式都是命令式编程（imperative programming），即面向算法过程，由程序员通过编程告诉计算机应采取的动作。然后，计算机按程序指令执行一系列流程，生成特定的结果，就像菜谱指定了厨师做蛋糕时应遵循的一系列步骤一样。</p>
<p>从Go看 C，没有面向对象，没有函数式编程，没有泛型（Go 1.18已加入），满眼都是类型与函数，可以说是相当亲切了。</p>
<h3>3. 错误处理机制如出一辙</h3>
<p>对于后端编程语言来说，错误处理机制十分重要。如果两种语言的错误处理机制不同，那么这两种语言的代码整体语法风格很可能大不相同。</p>
<p>在C语言中，我们通常用一个类型为整型的函数返回值作为错误状态标识，函数调用者基于值比较的方式，对这一代表错误状态的返回值进行检视。通常，当这个返回值为0时，代表函数调用成功；当这个返回值为其他值时，代表函数调用出现错误。函数调用者需根据该返回值所代表的错误状态，来决定后续执行哪条错误处理路径上的代码。</p>
<p>C语言这种简单的<strong>基于错误值比较</strong>的错误处理机制，让每个开发人员必须显式地去关注和处理每个错误。经过显式错误处理的代码会更为健壮，也会让开发人员对这些代码更有信心。另外，这些错误就是普通的值，我们不需要额外的语言机制去处理它们，只需利用已有的语言机制，像处理其他普通类型值那样去处理错误就可以了。这让代码更容易调试，我们也更容易针对每个错误处理的决策分支进行测试覆盖。</p>
<p>C语言错误处理机制的这种简单与显式，跟Go语言的设计哲学十分契合，于是Go语言设计者决定继承这种错误处理机制。因此，当Gopher们来到C语言的世界时，无需对自己的错误处理思维做出很大的改变，就可以很容易地适应C语言的风格。</p>
<h2>三. 知己知彼，来看看C与Go的差异</h2>
<p>虽说 Gopher 学习C语言有“先天优势”，但是不经过脚踏实地的学习与实践就想掌握和精通C语言，也是不可能的。而且，C 和Go还是有很大差异的，Gopher 们只有清楚这些差异，做到“知己知彼”，才能在学习过程中分清轻重，有的放矢。俗话说，“磨刀不误砍柴功”，下面我们就一起看看C与Go有哪些不同。</p>
<h3>1. 设计哲学</h3>
<p>在人类自然语言学界，有一个很著名的假说——“<a href="https://en.wikipedia.org/wiki/Linguistic_relativity">萨丕尔-沃夫假说</a>”。这个假说的内容是这样的：<strong>语言影响或决定人类的思维方式</strong>。对我来说，<strong>编程语言也不仅仅是一门工具，它还影响着程序员的思维方式</strong>。每次开始学习一门新的编程语言时，我都会先了解这门编程语言的设计哲学。</p>
<p>每种编程语言都有自己的设计哲学，即便这门语言的设计者没有将其显式地总结出来，它也真真切切地存在，并影响着这门语言的后续演进，以及这门语言程序员的思维方式。我在<a href="http://gk.link/a/10AVZ">《Tony Bai · Go语言第一课》</a>专栏里，将Go语言的设计哲学总结成了5点，分别是<strong>简单、显式、组合、并发和面向工程</strong>。</p>
<p>那么C语言的设计哲学又是什么呢？从表面上看，简单紧凑、性能至上、极致资源、全面移植，这些都可以作为C的设计哲学，但我倾向于一种更有人文气息的说法：<strong>满足和相信程序员</strong>。</p>
<p>在这样的设计哲学下，一方面，C语言提供了几乎所有可以帮助程序员表达自己意图的语法手段，比如宏、指针与指针运算、位操作、pragma指示符、goto语句，以及跳转能力更为强大的longjmp等；另一方面，C语言对程序员的行为并没有做特别严格的限定与约束，C程序员可以利用语言提供的这些语法手段，进行天马行空的发挥：访问硬件、利用指针访问内存中的任一字节、操控任意字节中的每个位（bit）等。总之，C语言假定程序员知道他们在做什么，并选择相信程序员。</p>
<p>C语言给了程序员足够的自由，可以说，在C语言世界，你几乎可以“为所欲为”。但这种哲学也是有代价的，那就是你可能会犯一些莫名其妙的错误，比如悬挂指针，而这些错误很少或不可能在其他语言中出现。</p>
<p>这里再用一个比喻来更为形象地表达下：从Go世界到C世界，就好比在动物园中饲养已久的动物被放归到野生自然保护区，有了更多自由，但周围也暗藏着很多未曾遇到过的危险。因此，学习C语言的Gopher们要有足够的心理准备。</p>
<h3>2. 内存管理</h3>
<p>接下来我们来看C与Go在内存管理方面的不同。我把这一点放在第二位，是因为这两种语言在内存管理上有很大的差异，而且这一差异会给程序员的日常编码带来巨大影响。</p>
<p>我们知道，Go是带有垃圾回收机制（俗称GC）的静态编程语言。使用Go编程时，内存申请与释放，在栈上还是在堆上分配，以及新内存块的清零等等，这一切都是自动的，且对程序员透明。</p>
<p>但在C语言中，上面说的这些都是程序员的责任。手工内存管理在带来灵活性的同时，也带来了极大的风险，其中最常见的就是内存泄露（memory leak）与悬挂指针（dangling pointer）问题。</p>
<p>内存泄露主要指的是<strong>程序员手工在堆上分配的内存在使用后没有被释放（free），进而导致的堆内存持续增加</strong>。而悬挂指针的意思是<strong>指针指向了非法的内存地址</strong>，未初始化的指针、指针所指对象已经被释放等，都是导致悬挂指针的主要原因。针对悬挂指针进行解引用（dereference）操作将会导致运行时错误，从而导致程序异常退出的严重后果。</p>
<p>Go语言带有GC，而C语言不带GC，这都是由各自语言设计哲学所决定的。GC是不符合C语言的设计哲学的，因为一旦有了GC，程序员就远离了机器，程序员直面机器的需求就无法得到满足了。并且，一旦有了GC，无论是在性能上还是在资源占用上，都不可能做到极致了。</p>
<p>在C中，手工管理内存到底是一种什么感觉呢？作为一名有着十多年C开发经验的资深C程序员，我只能告诉你：<strong>与内存斗，其乐无穷</strong>！这是在带GC的编程语言中无法体会到的。</p>
<h3>3. 语法形式</h3>
<p>虽然C语言是Go的先祖，并且Go也继承了很多C语言的语法元素，但在变量/函数声明、行尾分号、代码块是否用括号括起、标识符作用域，以及控制语句语义等方面，二者仍有较大差异。因此，对Go已经很熟悉的程序员在初学C时，受之前编码习惯的影响，往往会踩一些“坑”。基于此，我总结了Gopher学习C语言时需要特别注意的几点，接下来我们具体看看。</p>
<p><strong>第一，注意声明变量时类型与变量名的顺序</strong></p>
<p>前面说过，Go与C都是静态编译型语言，这就要求我们在使用任何变量之前，需要先声明这个变量。但Go采用的变量声明语法颇似Pascal语言，即<strong>变量名在前，变量类型在后</strong>，这与C语言恰好相反，如下所示：</p>
<pre><code>Go:

var a, b int
var p, q *int

vs.

C：
int a, b;
int *p, *q;
</code></pre>
<p>此外，Go支持短变量声明，并且由于短变量声明更短小，无需显式提供变量类型，Go编译器会根据赋值操作符后面的初始化表达式的结果，自动为变量赋予适当类型。因此，它成为了Gopher们喜爱和重度使用的语法。但短声明在C中却不是合法的语法元素：</p>
<pre><code>int main() {
    a := 5; //  error: expected expression
    printf("a = %d\n", a);
}
</code></pre>
<p>不过，和上面的变量类型与变量名声明的顺序问题一样，C编译器会发现并告知我们这个问题，并不会给程序带来实质性的伤害。</p>
<p><strong>第二，注意函数声明无需关键字前缀</strong></p>
<p>无论是C语言还是Go语言，函数都是基本功能逻辑单元，我们也可以说<strong>C程序就是一组函数的集合</strong>。实际上，我们日常的C代码编写大多集中在实现某个函数上。</p>
<p>和变量一样，函数在两种语言中都需要先声明才能使用。Go语言使用func关键字作为<strong>函数声明的前缀</strong>，并且函数返回值列表放在函数声明的最后。但在C语言中，函数声明无需任何关键字作为前缀，函数只支持单一返回值，并且返回值类型放在函数名的前面，如下所示：</p>
<pre><code>Go：
func Add(a, b int) int {
    return a+b
}

vs.

C：
int Add(int a, int b) {
    return a+b;
}
</code></pre>
<p><strong>第三，记得加上代码行结尾的分号</strong></p>
<p>我们日常编写Go代码时，<strong>极少手写分号</strong>。这是因为，Go设计者当初为了简化代码编写，提高代码可读性，选择了<strong>由编译器在词法分析阶段自动在适当位置插入分号的技术路线</strong>。如果你是一个被Go编译器惯坏了的Gopher，来到C语言的世界后，一定不要忘记代码行尾的分号。比如上面例子中的C语言Add函数实现，在return语句后面记得要手动加上分号。</p>
<p><strong>第四，补上“省略”的括号</strong></p>
<p>同样是出于简化代码、增加可读性的考虑，Go设计者最初就取消掉了条件分支语句（if）、选择分支语句（switch）和循环控制语句（for）中条件表达式外围的小括号：</p>
<pre><code>// Go代码
func f() int {
    return 5
}
func main() {
    a := 1
    if a == 1 { // 无需小括号包裹条件表达式
        fmt.Println(a)
    }

    switch b := f(); b { // 无需小括号包裹条件表达式
    case 4:
        fmt.Println("b = 4")
    case 5:
        fmt.Println("b = 5")
    default:
        fmt.Println("b = n/a")
    }

    for i := 1; i &lt; 10; i++ { // 无需小括号包裹循环语句的循环表达式
        a += i
    }
    fmt.Println(a)
}
</code></pre>
<p>这一点恰恰与C语言“背道而驰”。因此，我们在使用C语言编写代码时，务必要想着补上这些括号：</p>
<pre><code>// C代码
int f() {
        return 5;
}

int main() {
    int a = 1;
    if (a == 1) { // 需用小括号包裹条件表达式
        printf("%d\n", a);
    }

    int b = f();
    switch (b) { // 需用小括号包裹条件表达式
    case 4:
        printf("b = 4\n");
        break;
    case 5:
        printf("b = 5\n");
        break;
    default:
        printf("b = n/a\n");
    }

    int i = 0;
    for (i = 1; i &lt; 10; i++) { // 需用小括号包裹循环语句的循环表达式
        a += i;
    }
    printf("%d\n", a);
}
</code></pre>
<p><strong>第五，留意C与Go导出符号的不同机制</strong></p>
<p>C语言通过头文件来声明对外可见的符号，所以我们不用管符号是不是首字母大写的。但在Go中，只有首字母大写的包级变量、常量、类型、函数、方法才是可导出的，即对外部包可见。反之，首字母小写的则为包私有的，仅在包内使用。Gopher一旦习惯了这样的规则，在切换到C语言时，就会产生“心理后遗症”：遇到在其他头文件中定义的首字母小写的函数时，总以为不能直接使用。</p>
<p><strong>第六，记得在switch case语句中添加break</strong></p>
<p>C 语言与Go语言在选择分支语句的语义方面有所不同：C语言的 case 语句中，如果没有显式加入break语句，那么代码将向下自动掉落执行。而Go在最初设计时就重新规定了switch case的语义，默认不自动掉落（fallthrough），除非开发者显式使用fallthrough关键字。</p>
<p>适应了Go的switch case语句的语义后再回来写C代码，就会存在潜在的“风险”。我们来看一个例子：</p>
<pre><code>// C代码：
int main() {
    int a = 1;
    switch(a) {
        case 1:printf("a = 1\n");
        case 2:printf("a = 2\n");
        case 3:printf("a = 3\n");
        default:printf("a = ?\n");
    }
}
</code></pre>
<p>这段代码是按Go语义编写的switch case，编译运行后得到的结果如下：</p>
<pre><code>a = 1
a = 2
a = 3
a = ?
</code></pre>
<p>这显然不符合我们输出“a = 1”的预期。对于初学C的Gopher而言，这个问题影响还是蛮大的，因为这样编写的代码在C编译器眼中是完全合法的，但所代表的语义却完全不是开发人员想要的。这样的程序一旦流入到生产环境，其缺陷可能会引发生产故障。</p>
<p>一些Clint 工具可以检测出这样的问题，因此对于写C代码的Gopher，我建议在提交代码前使用lint工具对代码做一下检查。</p>
<h3>4. 构建机制</h3>
<p>Go与C都是静态编译型语言，它们的源码需要经过编译器和链接器处理，这个过程称为<strong>构建(build)</strong>，构建后得到的可执行文件才是最终交付给用户的成果物。</p>
<p>和Go语言略有不同的是，C语言的构建还有一个预处理（pre-processing）阶段，预处理环节的输出才是C编译器的真正输入。C语言中的宏就是在预处理阶段展开的。不过，Go没有预处理阶段。</p>
<p>C语言的编译单元是一个C源文件（.c），每个编译单元在编译过程中会对应生成一个目标文件（.o/.obj），最后链接器将这些目标文件链接在一起，形成可执行文件。</p>
<p>而Go则是以一个包（package）为编译单元的，每个包内的源文件生成一个.o文件，一个包的所有.o文件聚合（archive）成一个.a文件，链接器将这些目标文件链接在一起形成可执行文件。</p>
<p>Go语言提供了统一的Go命令行工具链，且Go编译器原生支持增量构建，源码构建过程不需要Gopher手工做什么配置。但在C语言的世界中，用于构建C程序的工具有很多，主流的包括gcc/clang，以及微软平台的C编译器。这些编译器原生不支持增量构建，为了提升工程级构建的效率，避免每次都进行全量构建，我们通常会使用第三方的构建管理工具，比如make（Makefile）或CMake。考虑移植性时，我们还会使用到configure文件，用于在目标机器上收集和设置编译器所需的环境信息。</p>
<h3>5. 依赖管理</h3>
<p>我在前面提过，C语言仅提供了一个“小内核”。像依赖管理这类的事情，C语言本身并没有提供跟Go中的Go Module类似的，统一且相对完善的解决方案。在C语言的世界中，我们依然要靠外部工具（比如CMake）来管理第三方的依赖。</p>
<p>C语言的第三方依赖通常以静态库（.a）或动态共享库（.so）的形式存在。如果你的应用要使用静态链接，那就必须在系统中为C编译器提供第三方依赖的静态库文件。但在实际工作中，完全采用静态链接有时是会遇到麻烦的。这是因为，很多操作系统在默认安装时是不带开发包的，也就是说，像 libc、libpthread 这样的系统库只提供了动态共享库版本（如/lib下提供了libc的共享库libc.so.6），其静态库版本是需要自行下载、编译和安装的（如libc的静态库libc.a在安装后是放在/usr/lib下面的)。所以<strong>多数情况下，我们是将****静态、动态****两种链接方式混合在一起使用的</strong>，比如像libc这样的系统库多采用动态链接。</p>
<p>动态共享库通常是有版本的，并且按照一定规则安装到系统中。举个例子，一个名为libfoo的动态共享库，在安装的目录下文件集合通常是这样：</p>
<pre><code>2022-03-10 12:28 libfoo.so -&gt; libfoo.so.0.0.0*
2022-03-10 12:28 libfoo.so.0 -&gt; libfoo.so.0.0.0*
2022-03-10 12:28 libfoo.so.0.0.0*
</code></pre>
<p>按惯例，每个动态共享库都有多个名字属性，包括real name、soname和linker name。下面我们来分别看下。</p>
<ul>
<li>real name：实际包含共享库代码的那个文件的名字(如上面例子中的libfoo.so.0.0.0)。动态共享库的真实版本信息就在real name中，显然real name中的版本号符合<a href="https://semver.org/">语义版本规范</a>，即major.minor.patch。当两个版本的major号一致，说明是向后兼容的两个版本；</li>
<li>soname：shared object name的缩写，也是这三个名字中最重要的一个。无论是在编译阶段还是在运行阶段，系统链接器都是通过动态共享库的soname（如上面例子中的libfoo.so.0）来唯一识别共享库的。我们看到的soname实际上是仅包含major号的共享库名字；</li>
<li>linker name：编译阶段提供给编译器的名字（如上面例子中的libfoo.so）。如果你构建的共享库的real name跟上面例子中libfoo.so.0.0.0类似，带有版本号，那么你在编译器命令中直接使用-L path -lfoo是无法让链接器找到对应的共享库文件的，除非你为libfoo.so.0.0.0提供了一个linker name（如libfoo.so，一个指向libfoo.so.0.0.0的符号链接）。linker name一般在共享库安装时手工创建。<br />
动态共享库有了这三个名称属性，依赖管理就有了依据。但由于在链接的时候使用的是linker name，而linker name并不带有版本号，真实版本与主机环境有关，因此要实现C应用的可重现构建还是比较难。在实践中，我们通常会使用专门的构建主机，项目组将该主机上的依赖管理起来，进而保证每次构建所使用的依赖版本是可控的。同时，应用部署的目标主机上的依赖版本也应该得到管理，避免运行时出现动态共享库版本不匹配的问题。</li>
</ul>
<h3>6. 代码风格</h3>
<p>Go语言是历史上首次实现了代码风格全社区统一的编程语言。它基本上消除了开发人员在代码风格上的无休止的、始终无法达成一致的争论，以及不同代码风格带来的阅读、维护他人代码时的低效。gofmt工具格式化出来的代码风格已经成为Go开发者的一种共识，融入到Go语言的开发文化当中了。所以，如果你让某个Go开发者说说gofmt后的代码风格是什么样的，多数Go开发者可能说不出，因为代码会被gofmt自动变成那种风格，大家已经不再关心风格了。</p>
<p>而在C语言的世界，代码风格仍存争议。但经过多年的演进，以及像Go这样新兴语言的不断“教育”，C社区也在尝试进行这方面的改进，涌现出了像<a href="https://clang.llvm.org/docs/ClangFormat.html">clang-format</a>这样的工具。目前，虽然还没有在全社区达成一致的代码风格（由于历史原因，这很难做到），但已经可以减少很多不必要的争论。</p>
<p>对于正在学习C语言，并进行C编码实践的Gopher，我的建议是：<strong>不要拘泥于使用什么代码风格，先用clang-format，并确定一套风格模板就好</strong>。</p>
<h2>四. 小结</h2>
<p>作为一名对Go跟随和研究了近十年的程序员，我深刻体会到，Go的简单性、性能和生产力使它成为了创建面向用户的应用程序和服务的理想语言。快速的迭代让团队能够快速地作出反应，以满足用户不断变化的需求，让团队可以将更多精力集中在保持灵活性上。</p>
<p>但Go也有缺点，比如缺少对内存以及一些低级操作的精确控制，而C语言恰好可以弥补这个缺陷。C 语言提供的更精细的控制允许更多的精确性，使得C成为低级操作的理想语言。这些低级操作不太可能发生变化，并且C相比Go还提高了性能。所以，如果你是一个有性能与低级操作需求的 Gopher ，就有充分的理由来学习C语言。</p>
<p>C 的优势体现在最接近底层机器的地方，而Go的优势在离用户较近的地方能得到最大发挥。当然，这并不是说两者都不能在对方的空间里工作，但这样做会增加“摩擦”。当你的需求从追求灵活性转变为注重效率时，用C重写库或服务的理由就更充分了。</p>
<p>总之，虽然Go和C的设计有很大的不同，但它们也有很多相似性，具备发挥兼容优势的基础。并且，当我们同时使用这二者时，就可以既有很大的灵活性，又有很好的性能，可以说是相得益彰！</p>
<h2>五. 写在最后</h2>
<p>今天的加餐中，我主要是基于C与Go的比较来讲解的，对于Go语言的特性并没有作详细展开。如果你还想进一步了解Go语言的设计哲学、语法特性、程序设计相关知识，欢迎来学习我在极客时间上的专栏<a href="http://gk.link/a/10AVZ">《Tony Bai ·Go语言第一课》</a>。在这门课里，我会用我十年Gopher的经验，带给你一条系统、完整的Go语言入门路径。</p>
<p>感谢你看到这里，如果今天的内容让你有所收获，欢迎把它分享给你的朋友。</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>微信公众号：iamtonybai</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>“Gopher部落”知识星球：https://public.zsxq.com/groups/51284458844544</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/05/16/the-short-guide-of-embracing-c-lang-for-gopher/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>究竟是什么让Go语言成为恶意软件作者的最爱</title>
		<link>https://tonybai.com/2021/03/07/go-malware-round-up-2020/</link>
		<comments>https://tonybai.com/2021/03/07/go-malware-round-up-2020/#comments</comments>
		<pubDate>Sun, 07 Mar 2021 09:13:13 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[asset]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[embed]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.16]]></category>
		<category><![CDATA[Go1.5]]></category>
		<category><![CDATA[go1.8]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[InfluxDB]]></category>
		<category><![CDATA[intezer]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[malware]]></category>
		<category><![CDATA[RAT]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[RSA]]></category>
		<category><![CDATA[shellcode]]></category>
		<category><![CDATA[stdlib]]></category>
		<category><![CDATA[syscall]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[僵尸网络]]></category>
		<category><![CDATA[加密]]></category>
		<category><![CDATA[勒索软件]]></category>
		<category><![CDATA[区块链]]></category>
		<category><![CDATA[可移植性]]></category>
		<category><![CDATA[后门]]></category>
		<category><![CDATA[嵌入文件]]></category>
		<category><![CDATA[恶意软件]]></category>
		<category><![CDATA[挖矿]]></category>
		<category><![CDATA[木马]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[比特币]]></category>
		<category><![CDATA[物联网]]></category>
		<category><![CDATA[自举]]></category>
		<category><![CDATA[跨平台编译]]></category>
		<category><![CDATA[运行时]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3117</guid>
		<description><![CDATA[2020年5月份，Go语言之父Rob Pike接受了evrone.com的专访。当Rob Pike老爷子被问及多年来他看到过最奇怪、最有创意或有趣的Go用法或最让他惊讶的是什么时，老爷子是这么回答的： Rob：最大的惊喜是当我们得知Go被用于编写恶意软件时。您无法控制谁将使用您的作品或他们将如何使用它。 近期安全技术公司Intezer发布了一份名为《Year of the Gopher, A 2020 Go Malware Round-Up》的报告，该报告称在过去几年中，安全人员发现的用Go编写的新恶意软件几乎增加了2000%，这一标题迅速引爆程序员社区，有人唾弃Go踏入“歧途”，也有人膜拜Go的niubility：能被黑客看中和使用的都是精华！ 那么究竟是什么让黑客们这么青睐Go并用之去编写恶意软件呢？估计但那份几十页的报告没几个人会完整的读一遍，本文我们就结合报告的内容(分类、整理、摘录)做一些探究。 1. Go语言的简介 报告首先简单介绍了Go的前世今生。 Go是一种开源的编程语言，由Robert Griesemer、Rob Pike和Ken Thompson于2007年在Google开发。它于2009年11月向公众发布。开发新语言的动机来自于使用当前编程语言(当时三巨头都是用C++)的挫折感。由于CPU不再通过增加时钟周期的数量来提高速度。相反，更多的速度开始通过添加更多的CPU核并允许更多的并行执行来获得。这种硬件上的进化并没有很好地反映在通用编程语言中。虽然C、C++和Java等语言提供了在多核上并行执行事务的功能，但它们为程序员提供的帮助却很少，无法高效、安全地完成这项工作。 Google的程序员们于是开始设计一种新的编程语言，为方便和安全的使用并发或并行提供“原生/一等公民地位”的支持。另一个目标则是要将解释型语言的编程便利性与静态类型和编译型语言的效率和安全性结合起来。另外在设计时，Google是将其用于Google基础设施运行的一部分网络服务中，因此对网络的支持也很重要。 为了提供在解释语言中编程的感觉，Go使用垃圾收集并处理所有的内存管理。所有的Go二进制文件都包含一个称为运行时的通用库，这导致Go二进制文件的大小比用C语言编写的类似的静态链接的程序要大。该库负责处理垃圾收集、执行线程的调度以及该语言的所有其他关键功能。虽然它被称为运行时，但比起Java运行时，它更像C语言的libc，它已经与二进制文件进行了静态编译。Go二进制文件被编译成本地机器代码，但也可以被编译成以JavaScript为运行时的WebAssembly。 Go 1.4版本及更早版本的编译器是用C语言实现的，但随着2015年1.5版本的发布，编译器完全用Go语言编写，并实现了自举。转为自举编译器后，给用户在交叉编译方面的体验带来了巨大的改善。之前使用基于C语言的编译器时，需要在编译代码的机器上安装一个针对目标操作系统和架构的C编译器。和针对不同目标的C代码进行交叉编译时的方式非常相似。从1.5版本开始，只需要向编译器指明它的编译目标架构，就可以实现对不同操作系统和架构的交叉编译。不需要针对目标的特殊编译器。Go可以通过不依赖主机上的库来执行例如syscalls(系统调用)。本来由libc提供的功能由Go的标准库提供和处理。这种方便的交叉编译有一个限制，那就是当Go程序需要通过其外函数接口（FFI）与C语言编写的库进行交互时。 新的功能和解决方案使得程序员在新项目中采用Go。2016年，TIOBE授予Go“年度最佳编程语言”，这是一个授予评分上升幅度最高的语言的奖项。随着软件开发者因其功能而开始采用Go，恶意软件作者也开始采用Go也就不足为奇了。 人们注意到使用Go开发的恶意软件增多是从2019年Palo Alto Networks公司发布的一份分析报告开始的。2019年7月，Palo Alto Networks公司的Unit 42发布了对当时发现的用Go编写的恶意软件的分析报告。研究发现，2017年至2019年期间，人们发现的Go恶意软件样本增加 了1944%，这量化了一个很容易发现的趋势。在2019年之前，发现用Go编写的恶意软件更多的是一种罕见的现象，而在2019年期间，这成为了一种日常现象。报告中分析的恶意软件中，大部分，92%的恶意软件针对Windows，而4.5%针对Linux，3.5%针对macOS。 人们观察到的另一点是，渗透测试(pen-testing)团队采用Go来开发他们的工具，这在Unit 42的研究中很突出。 最常见的恶意软件家族类型是开源或渗透测试后门。其次是coinminer(挖矿)、窃取者和僵尸网络。这篇报告涵盖了2020年期间活跃的用Go编写的已知恶意软件的活动。 2. 使用Go的嵌入文件功能实现恶意加载器 与其他语言产生的二进制文件相比，Go编译器产生的二进制文件相对较大。例如，一个Hello World二进制文件有1700多个函数。由于二进制文件中有这么多的常用代码，因此在寻找可疑代码时就像大海捞针一样。这可能是为什么恶意Go二进制文件有时不被 反病毒引擎检测到的原因之一。这导致一些威胁行为者在Go中开发加载器，并利用它们来提供其他较老的、易被检测到的恶意软件。这种技术可以降低被检出率，甚至有时会使恶意软件完全无法被检测到。在Go二进制文件中嵌入其他二进制文件相对容易。有很多开源库已经解决了这个问题。下面是其中的一些列表： https://github.com/gobuffalo/packr https://github.com/rakyll/statik https://github.com/GeertJohan/go.rice https://github.com/UnnoTed/fileb0x https://github.com/mjibson/esc https://github.com/kevinburke/go-bindata https://github.com/lu4p/binclude https://github.com/omeid/go-resources https://github.com/pyros2097/go-embed https://github.com/wlbr/mule https://github.com/miscing/embed https://github.com/kyioptr/gassets 上述包的大部分的设计都是为了允许嵌入网络服务的静态资源文件(asset)，但使用案例并不限于此。嵌入文件的功能受到了广泛的好评，以至于今年2020年早些时候有人建议将该功能直接添加到Go编译器中。该建议已被接受，并已与2021年2月发布的Go 1.16版本一起发布了。从这个角度来看，Go [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-malware-2020-1.png" alt="" /></p>
<p>2020年5月份，<a href="https://mp.weixin.qq.com/s/GHmwueTXNcZRo2oaF0Fqzg">Go语言之父Rob Pike接受了evrone.com的专访</a>。当Rob Pike老爷子被问及多年来他看到过最奇怪、最有创意或有趣的Go用法或最让他惊讶的是什么时，老爷子是这么回答的：</p>
<blockquote>
<p>Rob：最大的惊喜是当我们得知Go被用于编写恶意软件时。您无法控制谁将使用您的作品或他们将如何使用它。</p>
</blockquote>
<p>近期安全技术公司Intezer发布了一份名为<a href="https://www.intezer.com/wp-content/uploads/2021/02/Intezer-2020-Go-Malware-Round-Up.pdf">《Year of the Gopher, A 2020 Go Malware Round-Up》</a>的报告，该报告称在过去几年中，安全人员发现的<strong>用Go编写的新恶意软件几乎增加了2000%</strong>，这一标题迅速引爆程序员社区，有人唾弃Go踏入“歧途”，也有人膜拜Go的niubility：能被黑客看中和使用的都是精华！</p>
<p>那么究竟是什么让黑客们这么青睐Go并用之去编写恶意软件呢？估计但那份几十页的报告没几个人会完整的读一遍，本文我们就结合报告的内容(分类、整理、摘录)做一些探究。</p>
<h3>1. Go语言的简介</h3>
<p>报告首先简单介绍了<a href="https://www.imooc.com/read/87/article/2320">Go的前世今生</a>。</p>
<p>Go是一种开源的编程语言，由Robert Griesemer、Rob Pike和Ken Thompson于2007年在Google开发。它于2009年11月向公众发布。开发新语言的动机来自于使用当前编程语言(当时三巨头都是用C++)的挫折感。由于CPU不再通过增加时钟周期的数量来提高速度。相反，更多的速度开始通过添加更多的CPU核并允许更多的并行执行来获得。这种硬件上的进化并没有很好地反映在通用编程语言中。虽然C、C++和Java等语言提供了在多核上并行执行事务的功能，但它们为程序员提供的帮助却很少，无法高效、安全地完成这项工作。</p>
<p>Google的程序员们于是开始设计一种新的编程语言，为方便和安全的使用并发或并行提供“原生/一等公民地位”的支持。另一个目标则是要将解释型语言的编程便利性与静态类型和编译型语言的效率和安全性结合起来。另外在设计时，Google是将其用于Google基础设施运行的一部分网络服务中，因此对网络的支持也很重要。</p>
<p>为了提供在解释语言中编程的感觉，Go使用垃圾收集并处理所有的内存管理。所有的Go二进制文件都包含一个称为运行时的通用库，这导致Go二进制文件的大小比用C语言编写的类似的静态链接的程序要大。该库负责处理垃圾收集、执行线程的调度以及该语言的所有其他关键功能。虽然它被称为运行时，但比起Java运行时，它更像C语言的libc，它已经与二进制文件进行了静态编译。Go二进制文件被编译成本地机器代码，但也可以被编译成以JavaScript为运行时的WebAssembly。</p>
<p>Go 1.4版本及更早版本的编译器是用C语言实现的，但随着2015年1.5版本的发布，编译器完全用Go语言编写，并实现了自举。转为自举编译器后，给用户<a href="https://dave.cheney.net/2015/03/03/cross-compilation-just-got-a-whole-lot-better-in-go-1-5">在交叉编译方面的体验带来了巨大的改善</a>。之前使用基于C语言的编译器时，需要在编译代码的机器上安装一个针对目标操作系统和架构的C编译器。和针对不同目标的C代码进行交叉编译时的方式非常相似。从1.5版本开始，只需要向编译器指明它的编译目标架构，就可以实现对不同操作系统和架构的交叉编译。不需要针对目标的特殊编译器。Go可以通过不依赖主机上的库来执行例如syscalls(系统调用)。本来由libc提供的功能由Go的标准库提供和处理。这种方便的交叉编译有一个限制，那就是当Go程序需要通过其外函数接口（FFI）与C语言编写的库进行交互时。</p>
<p>新的功能和解决方案使得程序员在新项目中采用Go。2016年，TIOBE授予Go“年度最佳编程语言”，这是一个授予评分上升幅度最高的语言的奖项。随着软件开发者因其功能而开始采用Go，恶意软件作者也开始采用Go也就不足为奇了。</p>
<p>人们注意到使用Go开发的恶意软件增多是从2019年Palo Alto Networks公司发布的一份分析报告开始的。2019年7月，Palo Alto Networks公司的Unit 42发布了<a href="https://unit42.paloaltonetworks.com/the-gopher-in-the-room-analysis-of-golang-malware-in-the-wild/">对当时发现的用Go编写的恶意软件的分析报告</a>。研究发现，2017年至2019年期间，人们发现的Go恶意软件样本增加 了1944%，这量化了一个很容易发现的趋势。在2019年之前，发现用Go编写的恶意软件更多的是一种罕见的现象，而在2019年期间，这成为了一种日常现象。报告中分析的恶意软件中，大部分，92%的恶意软件针对Windows，而4.5%针对Linux，3.5%针对macOS。<br />
人们观察到的另一点是，渗透测试(pen-testing)团队采用Go来开发他们的工具，这在Unit 42的研究中很突出。</p>
<p>最常见的恶意软件家族类型是开源或渗透测试后门。其次是coinminer(挖矿)、窃取者和僵尸网络。这篇报告涵盖了2020年期间活跃的用Go编写的已知恶意软件的活动。</p>
<h3>2. 使用Go的嵌入文件功能实现恶意加载器</h3>
<p>与其他语言产生的二进制文件相比，Go编译器产生的二进制文件相对较大。例如，一个Hello World二进制文件有1700多个函数。由于二进制文件中有这么多的常用代码，因此在寻找可疑代码时就像大海捞针一样。这可能是为什么恶意Go二进制文件有时不被 反病毒引擎检测到的原因之一。这导致一些威胁行为者在Go中开发加载器，并利用它们来提供其他较老的、易被检测到的恶意软件。这种技术可以降低被检出率，甚至有时会使恶意软件完全无法被检测到。在Go二进制文件中嵌入其他二进制文件相对容易。有很多开源库已经解决了这个问题。下面是其中的一些列表：</p>
<ul>
<li>https://github.com/gobuffalo/packr</li>
<li>https://github.com/rakyll/statik</li>
<li>https://github.com/GeertJohan/go.rice</li>
<li>https://github.com/UnnoTed/fileb0x</li>
<li>https://github.com/mjibson/esc</li>
<li>https://github.com/kevinburke/go-bindata </li>
<li>https://github.com/lu4p/binclude</li>
<li>https://github.com/omeid/go-resources</li>
<li>https://github.com/pyros2097/go-embed </li>
<li>https://github.com/wlbr/mule</li>
<li>https://github.com/miscing/embed</li>
<li>https://github.com/kyioptr/gassets</li>
</ul>
<p>上述包的大部分的设计都是为了允许嵌入网络服务的静态资源文件(asset)，但使用案例并不限于此。嵌入文件的功能受到了广泛的好评，以至于今年2020年早些时候有人建议将该功能直接添加到Go编译器中。该建议已被接受，并已与2021年2月发布的<a href="https://mp.weixin.qq.com/s/UPNn4G_m0zfvJgWxEVl_Tw">Go 1.16版本</a>一起发布了。从这个角度来看，Go 1.16版本加入嵌入文件功能，颇有些“助纣为虐”之嫌^_^。</p>
<h3>3. 使用Go标准库强大的加密库和便捷的跨主机交叉编译特性实现恶意加密器和勒索软件</h3>
<p>Go的标准库提供了一套非常强大的加密库，允许开发者在不需要使用任何第三方库的情况下，在应用中加入加密功能。</p>
<p>一个开源的加密加载器是<a href="https://github.com/EddieIvan01/gld">Go shellcode LoaDer</a>。它用AES对有效载荷进行加密。它对有效载荷进行解密，并在执行之前使用ZwProtectVirtualMemory将解密缓冲区标记为读取/执行。</p>
<p>我们还观察到威胁行为者编写自己的加密器和加载器。例如，我们看到一个名为gocrypter的加载器被用于加密商品恶意软件；大多数是RAT(Remote Access Trojans，远程访问木马)和键盘记录器。有效载荷已经用AES加密，并作为base64编码的blob存储在二进制内部。加密器将其解码成字节，并在写入磁盘和执行之前进行解密。</p>
<p>在2020年仍有一些活动的勒索软件，比如：RobbinHood。RobbinHood在2019年春季被发现，当巴尔的摩市被发现受到该勒索软件攻击时，得到了很多媒体的关注。Sophos在2月份发布了一份报告，详细介绍了该威胁行为者的一些演变过程。通过利用技嘉公司的一个脆弱的驱动程序，威胁行为者开始加载一个未签名的驱动程序。一旦驱动程序被加载，它将杀死进程和篡改保护软件，以确保勒索软件可以在不被中断的情况下加密硬盘驱动器的其余部分。但在2020年11月，仍有新的样本被发现，但勒索说明没有改变。11月的一个样本的PDB字符串为C:/Users/User/go/src/Robbinhood7，这表明根据恶意软件作者的说法，它可能是第7个版本的勒索软件。</p>
<p>另一个用Go编写的、仍然活跃的老牌勒索软件是Snatch。Snatch是在2018年12月被发现的，到现在似乎还在使用。该勒索软件由Snatch Team使用，他们通过远程访问服务（例如RDP）瞄准企业环境。一旦进入网络，该组织就会尝试在所有机器上部署勒索软件， 并对文件进行加密。该勒索软件在加密文件时有一个有趣的技术，该技术在2019年10月被引入到勒索软件中。该勒索软件将自己安装为一项服务，即使Windows启动到安全模式，也可以启动。在此之后，勒索软件将Windows重新启动到安全模式，允许它加密硬盘上的所有文件，而不会被安装的任何潜在的安全保护软件阻止。</p>
<p>Nefilim是一款勒索软件，最早出现在2020年3月。它是另一款名为Nemty的勒索软件的前身。最初的版本是用C++编写的，但在7月，该恶意软件用Go重新编写。除了加密受害者机器上的文件外，Nefilim背后的威胁行为者还窃取受害者的数据，并用于勒索。</p>
<p>由于Go提供了一种针对不同架构和操作系统交叉编译二进do制文件的简单方法，因此它被用于RaaS(Ransomware as a Service)勒索软件并不奇怪。它允许威胁行为者使用单一的代码库，以极低的工作量制作针对不同操作系统的二进制文件。Go已经被用于RaaS。在2020年的春天，一个新的RaaS被宣布，名为Smaug。Smaug是一个相对简单的勒索软件，但它为Windows、Linux和macOS提供”用户”的勒索软件服务。它可以在”企业”模式下运行，即所有机器使用一个密钥，或者每台机器模式下使用一个密钥。</p>
<p>Go可以为其他操作系统和架构制作二进制文件，这使得威胁行为者可以轻松地针对不同类型的设备，例如，嵌入式系统。在2019年夏天，我们发现了QNAPCrypt，也就是eCh0raix，这是一款针对QNAP NAS设备的勒索软件。后来，它还被用来针对Synology NAS设备。2020年，又发现了一款针对QNAP设备的新勒索软件。新的勒索软件被称为AgeLocker，因为它使用了开源的加密工具和库<a href="https://github.com/FiloSottile/age">age</a>。</p>
<p>在2020年期间发现的其他用Go编写的勒索软件包括。1月发现的Betasup，2月发现的Sorena也就是HackForLife和Vash，3月发现的GoGoogle。</p>
<h3>4. 使用Go优秀的网络协议栈实现开发RAT(远程访问木马)、恶意偷窃程序、恶意机器人和僵尸网络</h3>
<p>Go的网络协议栈写得非常好，易于操作。Go已经成为云计算的编程语言之一，很多云原生应用都是用它编写的。例如，Docker、Kubernetes、InfluxDB、Traefik、Terraform、CockroachDB、Prometheus和Consul都是用Go编写的。这是有道理的，因为创建Go背后的原因之一正是要发明一种更好的语言，可以用来取代Google内部使用的C++网络服务。因此远程访问木马(RAT)是用Go编写的，这并不奇怪。毕竟，它们非常需要优良的网络服务功能。</p>
<p>在这一年中，既有新的RAT出现，也有老的RAT不断被使用。早在2020年8月，我们发现了一个Linux版本的Carbanak威胁行为体使用的后门。该样本使用2017年2月发布的Go 1.8版本编译器进行编译。同样的编译器版本和构建环境被用于2017年RSA报告的一部分的初始Windows样本。</p>
<p>Glupteba是一个自2011年以来一直存在的恶意软件，但在2019年9月，发现了一个用Go改写的新版本。在整个2020年，这个新版本出现的更为频繁。该恶意软件在感染机器时，会尝试安装一个root-kit。为了绕过Windows中防止安装内核驱动程序的保护措施，恶意软件利用了一个脆弱的VirtualBox驱动程序。恶意软件会安装该驱动程序，由于该驱动程序是经过签名的，所以Windows会允许安装，并使用它在Ring-0中执行代码，以禁用Kernel Patch Protection（KPP）。这种技术并不新鲜，它最早被APT组织Turla使用。除此之外，该恶意软件还试图通过利用EternalBlue在本地网络内进行传播。</p>
<p>Windows并不是唯一一个被用Go编写的RAT攻击的操作系统。2020年10月，Bitdefender发布了一个针对Linux的新RAT的发现。Bitdefender的研究人员认为，它可能与2019年的PowerGhost活动有关。该威胁行为体针对的是易受CVE-2019-2725影响的WebLogic服务器。该RAT似乎被作者命名为NiuB。该恶意软件由两个二进制文件组成，即主恶意软件和一个防护恶意软件。该恶意软件收集受感染机器的信息，并将其发送到C2服务器。它可以执行shell命令，下载并执行其他二进制文件。</p>
<p>2020年1月，FireEye发布了一份针对NetScaler设备的攻击报告。攻击是利用CVE-2019-19781漏洞。作为攻击的一部分，威胁行为者使用了一种新的恶意软件，以前从未见过。FireEye将该恶意软件命名为NOTROBIN。它是用Go编写的，并被编译成在&#42;BSD上运行，这是NetScaler使用的底层操作系统。一个有趣的功能是，该恶意软件通过扫描新的NetScaler模板文件并将其删除来阻止其他恶意软件利用相同的漏洞，这些文件可能是作为利用尝试的一部分添加的。它在18634端口上打开一个UDP监听器，但忽略发送到它的数据。它基本上充当了一个mutex，以确保受感染的机器上只运行一个恶意软件的副本。</p>
<p>已经有一些用Go编写的窃取器。在2019年，Malwarebytes报告了一个名为CryptoStealer.Go的窃取器。它旨在窃取加密货币钱包和 存储在浏览器中的数据，如信用卡信息。</p>
<p>同样在2020年期间，发现了一个用Go编写的剪贴板窃取器。它似乎自2019年以来一直活跃。根据上传到VirusTotal的样本的文件名 ，该窃取器被伪装成黑客工具，表明它被用来针对其他威胁行为者。该恶意软件的设计很简单。它将自己安装在App/DataLocal/Support下，并隐藏文件或文件夹。它读取剪贴板并检查它是否看起来像加密货币地址。如果是，恶意软件就会用攻击者自己的比特币、莱特币、Monero或Ethereum钱包替换剪贴板内容。</p>
<p>该恶意软件中的比特币钱包地址自2018年秋季以来一直处于活跃状态。截至本文撰写时，它已经收到了534笔交易，价值近11BTC。</p>
<p>随着Go作为标准库的一部分支持许多网络协议，以及为不同架构编译二进制文件的便利性，越来越多的机器人用Go编写也就不足为奇了。另外，二进制文件包含了正常运行所需的一切，这也为代码作者提供了更多的保证，例如，它可以在不同的Linux发行版上运行。它不用担心机器上是否已经安装了库。因为它需要什么，就自带什么。还有很多第三方库，提供了访问其他服务的功能。</p>
<p>比如这里列出了一些机器人库，可以用来开发不同服务的机器人。</p>
<ul>
<li>https://github.com/go-joe/joe</li>
<li>https://github.com/bot-api/telegram</li>
<li>https://github.com/shomali11/slacker</li>
<li>https://github.com/go-chat-bot/bot</li>
<li>https://github.com/frodsan/fbot</li>
<li>https://github.com/go-telegram-bot-api/telegram-bot-api </li>
<li>https://github.com/tucnak/telebot</li>
</ul>
<p>随着开源机器人库的出现，它们被恶意软件作者滥用的情况并不少见。IRCFlu就是一个例子。IRCFlu是一个托管在GitHub上的IRC机器人。该机器人提供了在托管机器人的机器上执行任意代码的功能，这使得威胁行为者可以利用这个机器人远程控制多台受感染的机器。</p>
<p>除了开源项目被滥用外，2020年还出现了老牌知名僵尸网络的攻击行为。被称为ddg的僵尸网络是由Netlab在360首次报道的。他们在2017年10月检测到该僵尸网络对托管OrientDB的服务器的攻击。该僵尸网络的目标是安装Monero矿机。2020年，该僵尸网络进行了更新，通过增加一个p2p网络支持的C2基础设施，使其更有弹性地抵御击杀。混合的p2p网络基础设施允许威胁行为者在正常的C2服务器瘫痪时保持对机器人的控制。</p>
<p>另一个仍然活跃的老僵尸网络是StealthWorker，也被称为GoBrut。StealthWorker是Malwarebytes在2019年2月首次报道的。它是一个以Stealth Bomber为名在暗网论坛上销售的僵尸，用于通过凭证式蛮力攻击获得网络服务的访问权限。</p>
<p>僵尸网络r2r2是另一个通过蛮横强迫凭证传播的僵尸。它最早是在2018年被发现的。它随机生成IP地址，并试图通过弱凭证访问运行SSH的服务。一旦它获得了一个立足点，它就会在机器上安装一个密码器。该僵尸的功能非常有限，它由不到200百行的代码组成。</p>
<p>其他僵尸网络也在不断进化，以增加其潜在的目标。在2020年，Orthrus，也被称为Golang，演变为也针对Windows服务器。该僵尸是Antiy在2019年6月首次报道的。它主要针对未受保护或凭证薄弱的Redis服务器。一旦它获得远程代码执行，它就会安装一套二进制文件。一个是针对其他易受攻击服务的扫描器，一个看门狗服务和一个密码器。扫描器试图破坏其他有已知漏洞的网络服务。例如，Weblogic，Elasticsearch和Drupal是目标。在2020年，该恶意软件还增加了针对微软SQL服务器的目标。它试图通过强行获取凭证来获得访问权。该恶意软件包括一个近3000个密码的列表，它只针对SQL服务器使用。</p>
<p>12月，我们发现了另一个跨操作系统的挖掘机器人，我们称之为XMRig Miner Dropper。它的目标是运行MySQL、Tomcat和Jenkins的服务器以及凭证较弱或脆弱的WebLogic。根据底层操作系统的不同，该机器人提供了一个用于执行shell脚本或PowerShell脚本的有效载荷。一旦它入侵机器，它就会安装一个密码器，并试图利用其他服务器。</p>
<p>2016年9月，Mirai的源代码被发布。这导致许多新的僵尸网络从Mirai源代码中衍生出来。虽然该僵尸代码是用C++编写的，但该代码的发布为其他恶意软件作者用不同语言编写类似的僵尸提供了蓝本。2020年1月，Bitdefender发布了一份报告，介绍了一个用Go编写的受Mirai启发的新僵尸网络，他们将其命名为LiquorBot。该僵尸网络本质上是Mirai在Go中的重新实现，目标是运行在ARM（32位和64位）、x86（32位和64位）和MIPS上的Linux设备。该僵尸通过强行获取SSH证书和利用路由器的已知漏洞进行传播。一旦它获得了设备的访问权限，它就会试图感染其他人，并且还安装了一个Monero密码器。</p>
<p>LiquorBot并不是唯一受Mirai启发的僵尸网络。4月，我们发现了Kaiji，这是一个通过SSH蛮横强迫来针对Linux服务器和物联网设备的僵尸网络。除了强行插入薄弱的凭证外，该僵尸还试图使用在受感染机器上发现的本地SSH密钥来传播到企业内的其他机器。与Mirai类似，Kaiji允许僵尸管理员对他们选择的任何基础设施发起DDoS攻击。攻击包括两个TCPFlood实现（一个带有原始套接字）、两个UDPFlood实现（一个带有原始套接字）、IPSpoof攻击、SYNACK攻击、SYN攻击和ACK攻击。</p>
<p>2020年6月，Kaiji将其目标方法扩大到包括暴露API套接字的服务器。该恶意软件开始在互联网上扫描端口2375暴露的主机。如果它找到了一个，它会尝试部署一个流氓Docker容器，并在容器中执行Kaiji。</p>
<p>Kaiji不是唯一一个针对暴露的Docker API的僵尸网络。2020年11月，NetLab 360报告发现了一种名为Blackrota的新恶意软件。Kinsing，也被称为h2Miner，已经被称为针对Docker API。2020年1月，阿里巴巴云的研究人员首次报道了Kinsing。该僵尸网络正在使用masscan寻找暴露Hadoop Yarn、Redis和Docker的机器。当它发现一台运行这些服务的服务器时，它会试图利用服务中的已知漏洞来进一步传播自己。5月，我们观察到Kinsing利用SaltStack的两个漏洞CVE-2020-11651和CVE-2020-11652进行传播。该恶意软件还开始使用LD-PRELOAD用户地rootkit来隐藏其进程。</p>
<p>SSH brute-force已经成为用Go编写的僵尸网络采用的主要攻击方式之一。我们发现了IPStorm的一个新的Linux变种，其中包括这种攻击向量。IPStorm是一个点对点(p2p)僵尸网络，于2019年5月首次被发现。它使用开源项目IPFS作为其网络骨干。除了原始的Windows变体，我们还发现了作为Linux变体的一部分，针对Android和物联网设备的变体。与本报告中的其他僵尸网络不同，IPStorm的目标不是安装矿机。相反，该僵尸网络似乎提供了一个代理网络。这个代理网络是作为互联网上的匿名代理网络出售的。</p>
<p>IPStorm不是唯一一个在2020年活跃的Go编写的p2p网络。2020年8月，Guardicore发布了一份关于他们从同年1月开始追踪的一个新的p2p僵尸网络的报告。该僵尸网络被命名为FritzFrog，通过强行使用弱小的凭证来感染机器。Guardicore称，该僵尸网络已经成功入侵了超过500台服务器，其中包括 “美国和欧洲的知名高教机构，以及一家铁路公司”。</p>
<h3>5. 未来预测与结论</h3>
<p>虽然与用其他语言编写的恶意软件相比，用Go编写的恶意软件数量相对较少，但同比增长幅度很大。这种增长速度很可能会继续下去，这意味着用Go编写的恶意软件将变得更加频繁。对于针对Linux环境的恶意软件来说，用Go编写的部分比针对Windows的恶意软件要大。这很可能导致，在根据针对特定系统的恶意软件总量统计中，针对Linux系统的恶意软件的比例将可能变得最大。</p>
<p>在目前用Go编写的Linux恶意软件中，有很大一部分是用于DDoS或安装密码器的机器人。这种趋势可能会持续下去。其他类型也可能会变得更加频繁。我们已经看到了针对Linux系统的Go勒索软件，而且有可能会出现更多的以窃取和加密有价值数据为目标的勒索软件。这与Proofpoint对2021年的预测一致，即勒索软件威胁行为者将开始更加关注攻击云端。这意味着企业应该采用专注于云的检测和预防产品，以确保他们的云环境受到保护。许多传统的防病毒和保护解决方案都是为了保护Windows环境而设计的，而Linux环境则更多地成为了”二等公民”。</p>
<p>根据CrowdStrike从2020年开始的事件报告，在40%的事件中，恶意软件没有被反病毒产品检测到。除此之外，Go恶意软件一直很难被反病毒产品检测到，所以这种趋势很可能会继续下去。我们已经看到威胁行为者以相同的恶意软件代码库为中心，针对不同的操作系统进行攻击，导致恶意软件样本较少或未被检测到。由于恶意软件来自相同的代码库，因此使用代码基因的检测方法非常有效。未来我们很可能会看到更多针对多个操作系统的恶意软件，因为像Go这样的编程语言为恶意软件作者提供了一种简单的交叉编译恶意软件的方法。</p>
<p>在Windows方面，许多威胁行为者已经使用Go来制作勒索软件。未来这种趋势很可能会继续下去。随着更多RaaS产品的出现，用Go编写勒索软件也不是不可能。由于能够轻松地进行交叉编译，RaaS运营商可以为他们的”客户”提供更广泛的目标。</p>
<p>Go是一种开源的编程语言，它是在Google内部开发的，目的是利用过去几十年在硬件上取得的进步。它的设计是为了让开发者能够轻松地制作快速、安全、以网络为中心的代码，并在当今的多核CPU上获益。这使得该语言得到了极大的应用，尤其是在云环境中。开发者并不是唯一采用Go的人。<strong>Go强大的跨平台交叉编译、优秀的网络实现和加密库以及原生的文件嵌入功能让其颇受恶意软件开发者的青睐！</strong> 在过去几年中，<strong>在市面上发现的用Go编写的新恶意软件几乎增加了2000%</strong>。这些恶意软件中有许多是针对Linux和物联网设备的僵尸网络，以安装加密矿机或将受感染的机器注册到DDoS僵尸网络中。此外，用Go编写的勒索软件似乎也变得更加普遍。一些用Go编写的著名勒索软件是Nefilim、EKANS和RobbinHood，这些勒索软件用于所谓的大型猎物攻击。</p>
<p>传统的反病毒解决方案似乎仍然难以检测到用Go编写的恶意软件。较新的技术不仅可以根据代码重用来判断恶意，还可以对威胁进行分类，已经取得了较大的成功，因为它们甚至可以处理Linux和Windows二进制文件之间的相似性。虽然用Go编写的恶意软件可能仍处于初级阶段，但它可能很快就会进入青春期，从而导致大量增加。</p>
<hr />
<p><a href="https://mp.weixin.qq.com/s/jUqAL7hf2GmMun64BJufEA">“Gopher部落”知识星球</a>正式转正（从试运营星球变成了正式星球）！“gopher部落”旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！部落目前虽小，但持续力很强。在2021年上半年，部落将策划两个专题系列分享，并且是部落独享哦：</p>
<ul>
<li>Go技术书籍的书摘和读书体会系列</li>
<li>Go与eBPF系列</li>
</ul>
<p><img src="http://image.tonybai.com/img/202103/gopher-tribe-zsxq-card.png" alt="" /></p>
<p>Go技术专栏“<a href="https://www.imooc.com/read/87">改善Go语⾔编程质量的50个有效实践</a>”正在慕课网火热热销中！本专栏主要满足广大gopher关于Go语言进阶的需求，围绕如何写出地道且高质量Go代码给出50条有效实践建议，上线后收到一致好评！欢迎大家订阅！目前该技术专栏正在新春促销！关注我的个人公众号“iamtonybai”，发送“go专栏活动”即可获取专栏专属优惠码，可在订阅专栏时抵扣20元哦(2021.2月末前有效)。</p>
<p><img src="http://image.tonybai.com/img/202011/go-column-pgo-with-qr-and-text.png" alt="" /></p>
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网热卖中，欢迎小伙伴们订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-with-qr-and-text.png" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>微信公众号：iamtonybai</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>“Gopher部落”知识星球：https://public.zsxq.com/groups/51284458844544</li>
</ul>
<p>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2021, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2021/03/07/go-malware-round-up-2020/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Go语言对ARM架构的支持与未来[译]</title>
		<link>https://tonybai.com/2020/12/18/go-ports-until-202012/</link>
		<comments>https://tonybai.com/2020/12/18/go-ports-until-202012/#comments</comments>
		<pubDate>Fri, 18 Dec 2020 07:55:39 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[arm64]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[FreeBSD]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go-build]]></category>
		<category><![CDATA[go1.16]]></category>
		<category><![CDATA[GOARCH]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GOOS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[M1]]></category>
		<category><![CDATA[NetBSD]]></category>
		<category><![CDATA[OpenBSD]]></category>
		<category><![CDATA[plan9]]></category>
		<category><![CDATA[RISC-V]]></category>
		<category><![CDATA[Solaris]]></category>
		<category><![CDATA[SSA]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[x86]]></category>
		<category><![CDATA[x86-64]]></category>
		<category><![CDATA[交叉编译]]></category>
		<category><![CDATA[操作系统]]></category>
		<category><![CDATA[移植性]]></category>
		<category><![CDATA[苹果]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3041</guid>
		<description><![CDATA[本文翻译自Go官方博客文章《Go on ARM and Beyond》(https://blog.golang.org/ports)。 最近业界关于非x86处理器的讨论沸沸扬扬，所以我们认为值得简单的写一篇关于Go语言对这些非x86处理器的支持情况的文章。 对我们来说，Go的可移植性一直很重要，我们不会过度去适配任何特定的操作系统或架构。Go最初的开源版本包括对两种操作系统（Linux和MacOSX）和三种架构（64位x86、32位x86和32位ARM）的支持。 多年来，我们已经增加了对更多操作系统和架构组合的支持： Go1（2012年3月）支持原始系统(译注：上面提到的两种操作系统和三种架构)以及64位和32位x86上的FreeBSD、NetBSD和OpenBSD，以及32位x86上的Plan9。 Go 1.3（2014年6月）增加了对64位x86上Solaris的支持。 Go 1.4（2014年12月）增加了对32位ARM上Android和64位x86上Plan9的支持。 Go 1.5（2015年8月）增加了对64位ARM和64位PowerPC上的Linux以及32位和64位ARM上的iOS的支持。 Go 1.6（2016年2月）增加了对64位MIPS上的Linux，以及32位x86上的Android的支持。它还增加了32位ARM上的Linux官方二进制下载，主要用于RaspberryPi系统。 Go 1.7（2016年8月）增加了对的z系统（S390x）上Linux和32位x86上Plan9的支持。 Go 1.8（2017年2月）增加了对32位MIPS上Linux的支持，并且它增加了64位PowerPC和z系统上Linux的官方二进制下载。 Go 1.9（2017年8月）增加了对64位ARM上Linux的官方二进制下载。 Go 1.12（2018年2月）增加了对32位ARM上Windows10 IoT Core的支持，如RaspberryPi3。它还增加了对64位PowerPC上AIX的支持。 Go 1.14（2019年2月）增加了对64位RISC-V上Linux的支持。 虽然x86-64的移植在Go的早期得到了大部分的关注，但今天我们所有的目标架构都得到了我们基于SSA的编译器后端的良好支持，并生成了优秀的代码。我们一路走来得到了许多贡献者的帮助，包括来自Amazon、ARM、Atos、IBM、Intel和MIPS的工程师。 Go支持对所有这些系统进行开箱即用的交叉编译，而且只需付出最小的努力。例如，要在一个64位Linux系统中构建一个基于32位x86的Windows应用，我们只需执行下面命令： GOARCH=386 GOOS=windows go build myapp # 编译生成myapp.exe 在过去的一年里，几家主要的厂商都宣布了用于服务器、笔记本电脑和开发者机器的新ARM64硬件。Go在这些方面适配的很好。多年来，Go一直在ARM64 Linux服务器上为Docker、Kubernetes和Go生态系统的其他部分，以及ARM64 Android和iOS设备上的移动应用提供支持。 自今年夏天苹果宣布Mac过渡到苹果芯片以来，苹果和谷歌一直在合作，以确保Go和更广泛的Go生态系统在其上运行良好，无论是在Rosetta 2下运行Go x86二进制文件，还是运行原生Go ARM64二进制文件。本周早些时候，我们发布了第一个Go 1.16测试版，其中包括了对使用M1芯片的Mac的原生支持。您可以在Go下载页面上下载并试用适用于M1 Mac和所有其他系统的Go 1.16测试版。当然，这是一个测试版，就像所有的测试版一样，它肯定有我们不知道的bug。如果你遇到任何问题，请在golang.org/issue/new上报告）。 在本地开发中使用与生产中相同的CPU架构总是很好的，这样可以消除两种环境之间的差异。如果你部署到ARM64生产服务器上，Go也可以轻松在ARM64 Linux和Mac系统上进行开发。但当然，无论你是在x86系统上工作并部署到ARM上，还是在Windows上工作并部署到Linux上，或者其他组合，在一个系统上工作并交叉编译部署到另一个系统上仍然和以前一样容易。 我们希望添加支持的下一个目标是ARM64 Windows 10系统。如果你有专业知识并愿意提供帮助，我们正在golang.org/issue/36439上协调工作。 “Gopher部落”知识星球开球了！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！星球首开，福利自然是少不了的！2020年年底之前，8.8折(很吉利吧^_^)加入星球，下方图片扫起来吧！ 我的Go技术专栏：“改善Go语⾔编程质量的50个有效实践”上线了，欢迎大家订阅学习！ [...]]]></description>
			<content:encoded><![CDATA[<p>本文翻译自Go官方博客文章<a href="https://blog.golang.org/ports">《Go on ARM and Beyond》</a>(https://blog.golang.org/ports)。</p>
<p>最近业界关于非x86处理器的讨论沸沸扬扬，所以我们认为值得简单的写一篇关于Go语言对这些非x86处理器的支持情况的文章。</p>
<p>对我们来说，<a href="https://tonybai.com/2017/06/27/an-intro-about-go-portability/">Go的可移植性</a>一直很重要，我们不会过度去适配任何特定的操作系统或架构。<a href="https://opensource.googleblog.com/2009/11/hey-ho-lets-go.html">Go最初的开源版本</a>包括对两种操作系统（Linux和MacOSX）和三种架构（64位x86、32位x86和32位ARM）的支持。</p>
<p>多年来，我们已经增加了对更多操作系统和架构组合的支持：</p>
<ul>
<li>Go1（2012年3月）支持原始系统(译注：上面提到的两种操作系统和三种架构)以及64位和32位x86上的FreeBSD、NetBSD和OpenBSD，以及32位x86上的Plan9。</li>
<li>Go 1.3（2014年6月）增加了对64位x86上Solaris的支持。</li>
<li><a href="https://tonybai.com/2014/11/04/some-changes-in-go-1-4/">Go 1.4</a>（2014年12月）增加了对32位ARM上Android和64位x86上Plan9的支持。</li>
<li><a href="https://tonybai.com/2015/07/10/some-changes-in-go-1-5/">Go 1.5</a>（2015年8月）增加了对64位ARM和64位PowerPC上的Linux以及32位和64位ARM上的iOS的支持。</li>
<li><a href="https://tonybai.com/2016/02/21/some-changes-in-go-1-6/">Go 1.6</a>（2016年2月）增加了对64位MIPS上的Linux，以及32位x86上的Android的支持。它还增加了32位ARM上的Linux官方二进制下载，主要用于RaspberryPi系统。</li>
<li><a href="https://tonybai.com/2016/06/21/some-changes-in-go-1-7/">Go 1.7</a>（2016年8月）增加了对的z系统（S390x）上Linux和32位x86上Plan9的支持。</li>
<li><a href="https://tonybai.com/2017/02/03/some-changes-in-go-1-8/">Go 1.8</a>（2017年2月）增加了对32位MIPS上Linux的支持，并且它增加了64位PowerPC和z系统上Linux的官方二进制下载。</li>
<li><a href="https://tonybai.com/2017/07/14/some-changes-in-go-1-9/">Go 1.9</a>（2017年8月）增加了对64位ARM上Linux的官方二进制下载。</li>
<li><a href="https://tonybai.com/2019/03/02/some-changes-in-go-1-12">Go 1.12</a>（2018年2月）增加了对32位ARM上Windows10 IoT Core的支持，如RaspberryPi3。它还增加了对64位PowerPC上AIX的支持。</li>
<li><a href="https://tonybai.com/2020/03/08/some-changes-in-go-1-14">Go 1.14</a>（2019年2月）增加了对64位RISC-V上Linux的支持。</li>
</ul>
<p>虽然x86-64的移植在Go的早期得到了大部分的关注，但今天我们所有的目标架构都得到了我们<a href="https://www.youtube.com/watch?v=uTMvKVma5ms">基于SSA的编译器后端</a>的良好支持，并生成了优秀的代码。我们一路走来得到了许多贡献者的帮助，包括来自Amazon、ARM、Atos、IBM、Intel和MIPS的工程师。</p>
<p>Go支持对所有这些系统进行开箱即用的<a href="https://tonybai.com/2014/10/20/cross-compilation-with-golang/">交叉编译</a>，而且只需付出最小的努力。例如，要在一个64位Linux系统中构建一个基于32位x86的Windows应用，我们只需执行下面命令：</p>
<pre><code>GOARCH=386 GOOS=windows go build myapp  # 编译生成myapp.exe
</code></pre>
<p>在过去的一年里，几家主要的厂商都宣布了用于服务器、笔记本电脑和开发者机器的新ARM64硬件。Go在这些方面适配的很好。多年来，Go一直在ARM64 Linux服务器上为Docker、Kubernetes和Go生态系统的其他部分，以及ARM64 Android和iOS设备上的移动应用提供支持。</p>
<p>自今年夏天苹果宣布Mac过渡到苹果芯片以来，苹果和谷歌一直在合作，以确保Go和更广泛的Go生态系统在其上运行良好，无论是在Rosetta 2下运行Go x86二进制文件，还是运行原生Go ARM64二进制文件。本周早些时候，我们发布了第一个Go 1.16测试版，其中包括了对使用M1芯片的Mac的原生支持。您可以在<a href="https://golang.google.cn/dl/#go1.16beta1">Go下载页面</a>上下载并试用适用于M1 Mac和所有其他系统的Go 1.16测试版。当然，这是一个测试版，就像所有的测试版一样，它肯定有我们不知道的bug。如果你遇到任何问题，请在golang.org/issue/new上报告）。</p>
<p>在本地开发中使用与生产中相同的CPU架构总是很好的，这样可以消除两种环境之间的差异。如果你部署到ARM64生产服务器上，Go也可以轻松在ARM64 Linux和Mac系统上进行开发。但当然，无论你是在x86系统上工作并部署到ARM上，还是在Windows上工作并部署到Linux上，或者其他组合，在一个系统上工作并交叉编译部署到另一个系统上仍然和以前一样容易。</p>
<p>我们希望添加支持的下一个目标是ARM64 Windows 10系统。如果你有专业知识并愿意提供帮助，我们正在golang.org/issue/36439上协调工作。</p>
<hr />
<p><strong>“Gopher部落”知识星球开球了！</strong>高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！星球首开，福利自然是少不了的！2020年年底之前，8.8折(很吉利吧^_^)加入星球，下方图片扫起来吧！</p>
<p><img src="http://image.tonybai.com/img/202011/gopher-tribe-zsxq.png" alt="" /></p>
<p>我的Go技术专栏：“<a href="https://www.imooc.com/read/87">改善Go语⾔编程质量的50个有效实践</a>”上线了，欢迎大家订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-column-pgo-with-qr-and-text.png" alt="img{512x368}" /></p>
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网热卖中，欢迎小伙伴们订阅学习！</p>
<p><img src="https://tonybai.com/wp-content/uploads/k8s-practice-with-qr-and-text.png" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家https://tonybai.com/<br />
smspush:可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展；短信内容你来定，不再受约束,接口丰富，支持长短信，签名可选。</p>
<p>2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5GRCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1coreCPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687开启你的DO主机之路。</p>
<p>GopherDaily(Gopher每日新闻)归档仓库-https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>微信公众号：iamtonybai</li>
<li>博客：tonybai.com</li>
<li>github:https://github.com/bigwhite</li>
<li>“Gopher部落”知识星球：https://public.zsxq.com/groups/51284458844544</li>
</ul>
<p>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2020, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2020/12/18/go-ports-until-202012/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Go 1.15中值得关注的几个变化</title>
		<link>https://tonybai.com/2020/10/11/some-changes-in-go-1-15/</link>
		<comments>https://tonybai.com/2020/10/11/some-changes-in-go-1-15/#comments</comments>
		<pubDate>Sun, 11 Oct 2020 03:10:06 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[arm64]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[Darwin]]></category>
		<category><![CDATA[GNU]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.11]]></category>
		<category><![CDATA[go1.13]]></category>
		<category><![CDATA[go1.14]]></category>
		<category><![CDATA[go1.15]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[gomodule]]></category>
		<category><![CDATA[GOPATH]]></category>
		<category><![CDATA[GOPROXY]]></category>
		<category><![CDATA[GOROOT]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[linker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Module]]></category>
		<category><![CDATA[objdump]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[panic]]></category>
		<category><![CDATA[Reflect]]></category>
		<category><![CDATA[RISC-V]]></category>
		<category><![CDATA[tzdata]]></category>
		<category><![CDATA[unsafe]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[zoneinfo]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[内存分配]]></category>
		<category><![CDATA[包]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[汇编]]></category>
		<category><![CDATA[编译器]]></category>
		<category><![CDATA[链接器]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=2955</guid>
		<description><![CDATA[Go 1.15版本在8月12日就正式发布了，给我的感觉就是发布的挺痛快^_^。这种感觉来自与之前版本发布时间的对比：Go 1.13版本发布于当年的9月4日，更早的Go 1.11版本发布于当年的8月25日。 不过这个时间恰与我家二宝出生和老婆月子时期有重叠，每天照顾孩子团团转的我实在抽不出时间研究Go 1.15的变化:(。如今，我逐渐从照顾二宝的工作中脱离出来^_^，于是“Go x.xx版本值得关注的几个变化”系列将继续下去。关注Go语言的演变对掌握和精通Go语言大有裨益，凡是致力于成为一名高级Gopher的读者都应该密切关注Go的演进。 截至写稿时，Go 1.15最新版是Go 1.15.2。Go 1.15一如既往的遵循Go1兼容性承诺。语言规范方面没有任何变化。可以说这是一个“面子”上变化较小的一个版本，但“里子”的变化还是不少的，在本文中我就和各位读者一起就重要变化逐一了解一下。 一. 平台移植性 Go 1.15版本不再对darwin/386和darwin/arm两个32位平台提供支持了。Go 1.15及以后版本仅对darwin/amd64和darwin/arm64版本提供支持。并且不再对macOS 10.12版本之前的版本提供支持。 Go 1.14版本中，Go编译器在被传入-race和-msan的情况下，默认会执行-d=checkptr，即对unsafe.Pointer的使用进行合法性检查。-d=checkptr主要检查两项内容： 当将unsafe.Pointer转型为&#42;T时，T的内存对齐系数不能高于原地址的； 做完指针算术后，转换后的unsafe.Pointer仍应指向原先Go堆对象 但在Go 1.14中，这个检查并不适用于Windows操作系统。Go 1.15中增加了对windows系统的支持。 对于RISC-V架构，Go社区展现出十分积极的姿态，早在Go 1.11版本，Go就为RISC-V cpu架构预留了GOARCH值：riscv和riscv64。Go 1.14版本则为64bit RISC-V提供了在linux上的实验性支持(GOOS=linux, GOARCH=riscv64)。在Go 1.15版本中，Go在GOOS=linux, GOARCH=riscv64的环境下的稳定性和性能得到持续提升，并且已经可以支持goroutine异步抢占式调度了。 二. 工具链 1. GOPROXY新增以管道符为分隔符的代理列表值 在Go 1.13版本中，GOPROXY支持设置为多个proxy的列表，多个proxy之间采用逗号分隔。Go工具链会按顺序尝试列表中的proxy以获取依赖包数据，但是当有proxy server服务不可达或者是返回的http状态码不是404也不是410时，go会终止数据获取。但是当列表中的proxy server返回其他错误时，Go命令不会向GOPROXY列表中的下一个值所代表的的proxy server发起请求，这种行为模式没能让所有gopher满意，很多Gopher认为Go工具链应该向后面的proxy server请求，直到所有proxy server都返回失败。Go 1.15版本满足了Go社区的需求，新增以管道符“&#124;”为分隔符的代理列表值。如果GOPROXY配置的proxy server列表值以管道符分隔，则无论某个proxy server返回什么错误码，Go命令都会向列表中的下一个proxy server发起新的尝试请求。 注：Go 1.15版本中GOPROXY环境变量的默认值依旧为https://proxy.golang.org,direct。 2. module cache的存储路径可设置 Go module机制自打在Go 1.11版本中以试验特性的方式引入时就将module的本地缓存默认放在了\$GOPATH/pkg/mod下（如果没有显式设置GOPATH，那么默认值将是~/go；如果GOPATH下面配置了多个路径，那么选择第一个路径），一直到Go [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-1.15-1.png" alt="img{512x368}" /></p>
<p><a href="https://tip.golang.org/doc/go1.15">Go 1.15版本</a>在8月12日就正式发布了，给我的感觉就是发布的挺痛快^_^。这种感觉来自与之前版本发布时间的对比：<a href="https://tonybai.com/2019/10/27/some-changes-in-go-1-13/">Go 1.13版本</a>发布于当年的9月4日，更早的<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11/">Go 1.11版本</a>发布于当年的8月25日。</p>
<p>不过这个时间恰与我家<a href="https://tonybai.com/2020/07/29/my-second-daughter-was-born/">二宝出生</a>和老婆月子时期有重叠，每天照顾孩子团团转的我实在抽不出时间研究Go 1.15的变化:(。如今，我逐渐从照顾二宝的工作中脱离出来^_^，于是“Go x.xx版本值得关注的几个变化”系列将继续下去。关注Go语言的演变对掌握和精通Go语言大有裨益，凡是致力于成为一名高级Gopher的读者都应该密切关注Go的演进。<br />
截至写稿时，Go 1.15最新版是Go 1.15.2。Go 1.15一如既往的遵循<a href="https://tip.golang.org/doc/go1compat.html">Go1兼容性承诺</a>。<a href="https://tip.golang.org/ref/spec">语言规范</a>方面没有任何变化。可以说这是一个“面子”上变化较小的一个版本，但“里子”的变化还是不少的，在本文中我就和各位读者一起就重要变化逐一了解一下。</p>
<h3>一. 平台移植性</h3>
<p>Go 1.15版本不再对darwin/386和darwin/arm两个32位平台提供支持了。Go 1.15及以后版本仅对darwin/amd64和darwin/arm64版本提供支持。并且不再对macOS 10.12版本之前的版本提供支持。</p>
<p><a href="https://tonybai.com/2020/03/08/some-changes-in-go-1-14/">Go 1.14版本</a>中，Go编译器在被传入-race和-msan的情况下，默认会执行<strong>-d=checkptr</strong>，即对unsafe.Pointer的使用进行<a href="https://github.com/golang/go/issues/34964">合法性检查</a>。<strong>-d=checkptr</strong>主要检查两项内容：</p>
<ul>
<li>
<p>当将unsafe.Pointer转型为&#42;T时，T的内存对齐系数不能高于原地址的；</p>
</li>
<li>
<p>做完指针算术后，转换后的unsafe.Pointer仍应指向原先Go堆对象</p>
</li>
</ul>
<p>但在Go 1.14中，这个检查并不适用于Windows操作系统。Go 1.15中增加了对windows系统的支持。</p>
<p>对于<a href="https://riscv.org">RISC-V</a>架构，Go社区展现出十分积极的姿态，早在<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11/">Go 1.11版本</a>，Go就为RISC-V cpu架构预留了GOARCH值：riscv和riscv64。<a href="https://tonybai.com/2020/03/08/some-changes-in-go-1-14/">Go 1.14版本</a>则为64bit RISC-V提供了在linux上的实验性支持(GOOS=linux, GOARCH=riscv64)。在Go 1.15版本中，Go在GOOS=linux, GOARCH=riscv64的环境下的稳定性和性能得到持续提升，并且已经可以支持goroutine异步抢占式调度了。</p>
<h3>二. 工具链</h3>
<h4>1. GOPROXY新增以管道符为分隔符的代理列表值</h4>
<p>在<a href="https://tonybai.com/2019/10/27/some-changes-in-go-1-13/">Go 1.13版本</a>中，<a href="https://tonybai.com/2018/11/26/hello-go-module-proxy/">GOPROXY</a>支持设置为多个proxy的列表，多个proxy之间采用逗号分隔。Go工具链会按顺序尝试列表中的proxy以获取依赖包数据，但是当有proxy server服务不可达或者是返回的http状态码不是404也不是410时，go会终止数据获取。但是当列表中的proxy server返回其他错误时，Go命令不会向GOPROXY列表中的下一个值所代表的的proxy server发起请求，这种行为模式没能让所有gopher满意，<strong>很多Gopher认为Go工具链应该向后面的proxy server请求，直到所有proxy server都返回失败</strong>。Go 1.15版本满足了Go社区的需求，新增以管道符“|”为分隔符的代理列表值。如果GOPROXY配置的proxy server列表值以管道符分隔，则无论某个proxy server返回什么错误码，Go命令都会向列表中的下一个proxy server发起新的尝试请求。</p>
<blockquote>
<p>注：Go 1.15版本中GOPROXY环境变量的默认值依旧为<strong>https://proxy.golang.org,direct</strong>。</p>
</blockquote>
<h4>2. module cache的存储路径可设置</h4>
<p><a href="https://tonybai.com/2019/06/03/the-practice-of-upgrading-major-version-under-go-module/">Go module机制</a>自打在<a href="https://tonybai.com/2018/11/19/some-changes-in-go-1-11/">Go 1.11版本</a>中以试验特性的方式引入时就将module的本地缓存默认放在了<strong>\$GOPATH/pkg/mod</strong>下（如果没有显式设置GOPATH，那么默认值将是<strong>~/go</strong>；如果GOPATH下面配置了多个路径，那么选择第一个路径），一直到Go 1.14版本，这个位置都是无法配置的。</p>
<p>Go module的引入为去除GOPATH提供了前提，于是module cache的位置也要尽量与GOPATH“脱钩”：Go 1.15提供了GOMODCACHE环境变量用于自定义module cache的存放位置。如果没有显式设置GOMODCACHE，那么module cache的默认存储路径依然是<strong>\$GOPATH/pkg/mod</strong>。</p>
<h3>三. 运行时、编译器和链接器</h3>
<h4>1. panic展现形式变化</h4>
<p>在Go 1.15之前，如果传给panic的值是bool, complex64, complex128, float32, float64, int, int8, int16, int32, int64, string, uint, uint8, uint16, uint32, uint64, uintptr等原生类型的值，那么panic在触发时会输出具体的值，比如：</p>
<pre><code>// go1.15-examples/runtime/panic.go

package main

func foo() {
    var i uint32 = 17
    panic(i)
}

func main() {
    foo()
}
</code></pre>
<p>使用Go 1.14运行上述代码，得到如下结果：</p>
<pre><code>$go run panic.go
panic: 17

goroutine 1 [running]:
main.foo(...)
    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:5
main.main()
    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:9 +0x39
exit status 2
</code></pre>
<p>Go 1.15版本亦是如此。但是对于派生于上述原生类型的自定义类型而言，Go 1.14只是输出变量地址：</p>
<pre><code>// go1.15-examples/runtime/panic.go

package main

type myint uint32

func bar() {
    var i myint = 27
    panic(i)
}

func main() {
    bar()
}
</code></pre>
<p>使用Go 1.14运行上述代码：</p>
<pre><code>$go run panic.go
panic: (main.myint) (0x105fca0,0xc00008e000)

goroutine 1 [running]:
main.bar(...)
    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:12
main.main()
    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:17 +0x39
exit status 2

</code></pre>
<p>Go 1.15针对此情况作了展示优化，即便是派生于这些原生类型的自定义类型变量，panic也可以输出其值。使用Go 1.15运行上述代码的结果如下：</p>
<pre><code>$go run panic.go
panic: main.myint(27)

goroutine 1 [running]:
main.bar(...)
    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:12
main.main()
    /Users/tonybai/go/src/github.com/bigwhite/experiments/go1.15-examples/runtime/panic.go:17 +0x39
exit status 2

</code></pre>
<h4>2. 将小整数([0,255])转换为interface类型值时将不会额外分配内存</h4>
<p>Go 1.15在runtime/iface.go中做了一些优化改动：增加一个名为staticuint64s的数组，预先为[0,255]这256个数分配了内存。然后在convT16、convT32等运行时转换函数中判断要转换的整型值是否小于256(len(staticuint64s))，如果小于，则返回staticuint64s数组中对应的值的地址；否则调用mallocgc分配新内存。</p>
<pre><code>$GOROOT/src/runtime/iface.go

// staticuint64s is used to avoid allocating in convTx for small integer values.
var staticuint64s = [...]uint64{
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
        0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
        0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
        0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,

        ... ...

        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
        0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,

}

func convT16(val uint16) (x unsafe.Pointer) {
        if val &lt; uint16(len(staticuint64s)) {
                x = unsafe.Pointer(&amp;staticuint64s[val])
                if sys.BigEndian {
                        x = add(x, 6)
                }
        } else {
                x = mallocgc(2, uint16Type, false)
                *(*uint16)(x) = val
        }
        return
}

func convT32(val uint32) (x unsafe.Pointer) {
        if val &lt; uint32(len(staticuint64s)) {
                x = unsafe.Pointer(&amp;staticuint64s[val])
                if sys.BigEndian {
                        x = add(x, 4)
                }
        } else {
                x = mallocgc(4, uint32Type, false)
                *(*uint32)(x) = val
        }
        return
}

</code></pre>
<p>我们可以用下面例子来验证一下：</p>
<pre><code>// go1.15-examples/runtime/tinyint2interface.go

package main

import (
    "math/rand"
)

func convertSmallInteger() interface{} {
    i := rand.Intn(256)
    var j interface{} = i
    return j
}

func main() {
    for i := 0; i &lt; 100000000; i++ {
        convertSmallInteger()
    }
}

</code></pre>
<p>我们分别用go 1.14和go 1.15.2编译这个源文件（注意关闭内联等优化，否则很可能看不出效果）：</p>
<pre><code>// go 1.14

go build  -gcflags="-N -l" -o tinyint2interface-go14 tinyint2interface.go

// go 1.15.2

go build  -gcflags="-N -l" -o tinyint2interface-go15 tinyint2interface.go

</code></pre>
<p>我们使用下面命令输出程序执行时每次GC的信息：</p>
<pre><code>$env GODEBUG=gctrace=1 ./tinyint2interface-go14
gc 1 @0.025s 0%: 0.009+0.18+0.021 ms clock, 0.079+0.079/0/0.20+0.17 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P
gc 2 @0.047s 0%: 0.003+0.14+0.013 ms clock, 0.031+0.099/0.064/0.037+0.10 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P
gc 3 @0.064s 0%: 0.008+0.20+0.016 ms clock, 0.071+0.071/0.018/0.081+0.13 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P
gc 4 @0.081s 0%: 0.005+0.14+0.013 ms clock, 0.047+0.059/0.023/0.032+0.10 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P
gc 5 @0.098s 0%: 0.005+0.10+0.017 ms clock, 0.042+0.073/0.027/0.080+0.13 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P

... ...

gc 192 @3.264s 0%: 0.003+0.11+0.013 ms clock, 0.024+0.060/0.005/0.035+0.11 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P
gc 193 @3.281s 0%: 0.005+0.13+0.032 ms clock, 0.042+0.075/0.041/0.050+0.25 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P
gc 194 @3.298s 0%: 0.004+0.12+0.013 ms clock, 0.033+0.072/0.030/0.033+0.10 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P
gc 195 @3.315s 0%: 0.003+0.17+0.023 ms clock, 0.029+0.062/0.055/0.024+0.18 ms cpu, 4-&gt;4-&gt;0 MB, 5 MB goal, 8 P

$env GODEBUG=gctrace=1 ./tinyint2interface-go15
</code></pre>
<p>我们看到和go 1.14编译的程序不断分配内存，不断导致GC相比，go1.15.2没有输出GC信息，间接证实了小整数转interface变量值时不会触发内存分配。</p>
<h4>3. 加入更现代化的链接器(linker)</h4>
<p>一个新版的<a href="https://golang.org/s/better-linker">现代化linker</a>正在逐渐加入到Go中，Go 1.15是新版linker的起点。后续若干版本，linker优化会逐步加入进来。在Go 1.15中，对于大型项目，新链接器的性能要提高20%，内存占用减少30%。</p>
<h4>4. objdump支持输出GNU汇编语法</h4>
<p>go 1.15为objdump工具增加了-gnu选项，<strong>以在Go汇编的后面，辅助输出GNU汇编，便于对照</strong>：</p>
<pre><code>// go 1.14：

$go tool objdump -S tinyint2interface-go15|more
TEXT go.buildid(SB)

  0x1001000             ff20                    JMP 0(AX)
  0x1001002             476f                    OUTSD DS:0(SI), DX
  0x1001004             206275                  ANDB AH, 0x75(DX)
  0x1001007             696c642049443a20        IMULL $0x203a4449, 0x20(SP), BP
... ...

//go 1.15.2：

$go tool objdump  -S -gnu tinyint2interface-go15|more
TEXT go.buildid(SB)

  0x1001000             ff20                    JMP 0(AX)                            // jmpq *(%rax)           

  0x1001002             476f                    OUTSD DS:0(SI), DX                   // rex.RXB outsl %ds:(%rsi),(%dx)
  0x1001004             206275                  ANDB AH, 0x75(DX)                    // and %ah,0x75(%rdx)     

  0x1001007             696c642049443a20        IMULL $0x203a4449, 0x20(SP), BP      // imul $0x203a4449,0x20(%rsp,%riz,2),%ebp

... ...

</code></pre>
<h3>四. 标准库</h3>
<p>和以往发布的版本一样，标准库有大量小改动，这里挑出几个笔者感兴趣的和大家一起看一下。</p>
<h4>1. 增加tzdata包</h4>
<p>Go time包中很多方法依赖时区数据，但不是所有平台上都自带时区数据。Go time包会以下面顺序搜寻时区数据：</p>
<pre><code>- ZONEINFO环境变量指示的路径中

- 在类Unix系统中一些常见的存放时区数据的路径（zoneinfo_unix.go中的zoneSources数组变量中存放这些常见路径）：

    "/usr/share/zoneinfo/",
    "/usr/share/lib/zoneinfo/",
    "/usr/lib/locale/TZ/"

- 如果平台没有，则尝试使用$GOROOT/lib/time/zoneinfo.zip这个随着go发布包一起发布的时区数据。但在应用部署的环境中，很大可能不会进行go安装。

</code></pre>
<p>如果go应用找不到时区数据，那么go应用运行将会受到影响，就如下面这个例子：</p>
<pre><code>// go1.15-examples/stdlib/tzdata.go

package main

import (
    "fmt"
    "time"
)

func main() {
    loc, err := time.LoadLocation("America/New_York")
    if err != nil {
        fmt.Println("LoadLocation error:", err)
        return
    }
    fmt.Println("LoadLocation is:", loc)
}

</code></pre>
<p>我们移除系统的时区数据(比如将/usr/share/zoneinfo改名)和Go安装包自带的zoneinfo.zip(改个名)后，在Go 1.15.2下运行该示例：</p>
<pre><code>$ go run tzdata.go
LoadLocation error: unknown time zone America/New_York

</code></pre>
<p>为此，Go 1.15提供了一个将时区数据嵌入到Go应用二进制文件中的方法：<strong>导入time/tzdata包</strong>：</p>
<pre><code>// go1.15-examples/stdlib/tzdata.go

package main

import (
    "fmt"
    "time"
    _ "time/tzdata"
)

func main() {
    loc, err := time.LoadLocation("America/New_York")
    if err != nil {
        fmt.Println("LoadLocation error:", err)
        return
    }
    fmt.Println("LoadLocation is:", loc)
}

</code></pre>
<p>我们再用go 1.15.2运行一下上述导入tzdata包的例子：</p>
<pre><code>$go run testtimezone.go
LoadLocation is: America/New_York

</code></pre>
<p>不过由于附带tzdata数据，应用二进制文件的size会增大大约800k，下面是在ubuntu下的实测值：</p>
<pre><code>-rwxr-xr-x 1 root root 2.0M Oct 11 02:42 tzdata-withouttzdata*
-rwxr-xr-x 1 root root 2.8M Oct 11 02:42 tzdata-withtzdata*

</code></pre>
<h4>2. 增加json解码限制</h4>
<p>json包是日常使用最多的go标准库包之一，在Go 1.15中，go按照json规范的要求，为json的解码增加了一层限制：</p>
<pre><code>// json规范要求

//https://tools.ietf.org/html/rfc7159#section-9

A JSON parser transforms a JSON text into another representation.  A
   JSON parser MUST accept all texts that conform to the JSON grammar.
   A JSON parser MAY accept non-JSON forms or extensions.

   An implementation may set limits on the size of texts that it
   accepts.  An implementation may set limits on the maximum depth of
   nesting.  An implementation may set limits on the range and precision
   of numbers.  An implementation may set limits on the length and
   character contents of strings.

</code></pre>
<p>这个限制就是增加了一个对json文本最大缩进深度值：</p>
<pre><code>// $GOROOT/src/encoding/json/scanner.go

// This limits the max nesting depth to prevent stack overflow.
// This is permitted by https://tools.ietf.org/html/rfc7159#section-9
const maxNestingDepth = 10000
</code></pre>
<p>如果一旦传入的json文本数据缩进深度超过maxNestingDepth，那json包就会panic。当然，绝大多数情况下，我们是碰不到缩进10000层的超大json文本的。因此，该limit对于99.9999%的gopher都没啥影响。</p>
<h4>3. reflect包</h4>
<p>Go 1.15版本之前reflect包<a href="https://github.com/golang/go/issues/38521">存在一处行为不一致的问题</a>，我们看下面例子(例子来源于https://play.golang.org/p/Jnga2_6Rmdf)：</p>
<pre><code>// go1.15-examples/stdlib/reflect.go

package main

import "reflect"

type u struct{}

func (u) M() { println("M") }

type t struct {
    u
    u2 u
}

func call(v reflect.Value) {
    defer func() {
        if err := recover(); err != nil {
            println(err.(string))
        }
    }()
    v.Method(0).Call(nil)
}

func main() {
    v := reflect.ValueOf(t{}) // v := t{}
    call(v)                   // v.M()
    call(v.Field(0))          // v.u.M()
    call(v.Field(1))          // v.u2.M()
}

</code></pre>
<p>我们使用Go 1.14版本运行该示例：</p>
<pre><code>$go run reflect.go
M
M
reflect: reflect.flag.mustBeExported using value obtained using unexported field

</code></pre>
<p>我们看到同为类型t中的非导出字段(field)的u和u2(u是以嵌入类型方式称为类型t的字段的)，通过reflect包可以调用字段u的导出方法(如输出中的第二行的M)，却无法调用非导出字段u2的导出方法（如输出中的第三行的panic信息）。</p>
<p>这种不一致在Go 1.15版本中被修复，我们使用Go 1.15.2运行上述示例：</p>
<pre><code>$go run reflect.go
M
reflect: reflect.Value.Call using value obtained using unexported field
reflect: reflect.Value.Call using value obtained using unexported field

</code></pre>
<p>我们看到reflect无法调用非导出字段u和u2的导出方法了。但是reflect依然可以通过提升到类型t的方法来间接使用u的导出方法，正如运行结果中的第一行输出。<br />
<strong>这一改动可能会影响到遗留代码中使用reflect调用以类型嵌入形式存在的非导出字段方法的代码</strong>，如果你的代码中存在这样的问题，可以直接通过提升(promote)到包裹类型(如例子中的t)中的方法（如例子中的call(v)）来替代之前的方式。</p>
<h3>五. 小结</h3>
<p>由于Go 1.15删除了一些GC元数据和一些无用的类型元数据，Go 1.15编译出的二进制文件size会减少5%左右。我用一个中等规模的go项目实测了一下：</p>
<pre><code>-rwxr-xr-x   1 tonybai  staff    23M 10 10 16:54 yunxind*
-rwxr-xr-x   1 tonybai  staff    24M  9 30 11:20 yunxind-go14*

</code></pre>
<p>二进制文件size的确有变小，大约4%-5%。</p>
<p><strong>如果你还没有升级到Go 1.15，那么现在正是时候</strong>。</p>
<p>本文中涉及的代码可以在<a href="https://github.com/bigwhite/experiments/tree/master/go1.15-examples">这里</a>下载。https://github.com/bigwhite/experiments/tree/master/go1.15-examples</p>
<hr />
<p>我的Go技术专栏：“<a href="https://www.imooc.com/read/87">改善Go语⾔编程质量的50个有效实践</a>”上线了，欢迎大家订阅学习！</p>
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网上线了，感谢小伙伴们学习支持！</p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/<br />
smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。</p>
<p>2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<p>微博：https://weibo.com/bigwhite20xx<br />
微信公众号：iamtonybai<br />
博客：tonybai.com<br />
github: https://github.com/bigwhite</p>
<p>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2020, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2020/10/11/some-changes-in-go-1-15/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
