<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>第 3 章 程序的生命周期 on Go 语言原本</title><link>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/</link><description>Recent content in 第 3 章 程序的生命周期 on Go 语言原本</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/index.xml" rel="self" type="application/rss+xml"/><item><title>3.1 从 `go` 命令谈起</title><link>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/cmd/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/cmd/</guid><description>&lt;h1 id="31-从-go-命令谈起"&gt;3.1 从 &lt;code&gt;go&lt;/code&gt; 命令谈起&lt;/h1&gt;
&lt;p&gt;Go 程序的生命周期要从执行 &lt;code&gt;go&lt;/code&gt; 命令开始谈起。读者每天敲下的 &lt;code&gt;go build&lt;/code&gt;、&lt;code&gt;go test&lt;/code&gt;、&lt;code&gt;go run&lt;/code&gt;，
看上去只是「把源码变成二进制」，背后却藏着一个常被误解的事实：&lt;code&gt;go&lt;/code&gt; 本身并不是编译器。它是一个
&lt;strong&gt;构建编排器&lt;/strong&gt;（build orchestrator），负责把一次构建分解成许多道工序，安排它们的先后与并行，
再逐道调用真正的工具，编译器 &lt;code&gt;compile&lt;/code&gt;（&lt;a href=".././compile"&gt;3.2&lt;/a&gt;）、汇编器 &lt;code&gt;asm&lt;/code&gt;、链接器 &lt;code&gt;link&lt;/code&gt;
（&lt;a href=".././link"&gt;3.4&lt;/a&gt;）。本节先把这层编排关系讲清楚，它是后续几节的总纲：读懂了 &lt;code&gt;go&lt;/code&gt; 如何把一次
构建拆成一张图、如何用内容寻址的缓存避免重复劳动，再去看编译与链接的细节，就有了落脚的框架。&lt;/p&gt;
&lt;h2 id="311-go-是构建编排器不是编译器"&gt;3.1.1 &lt;code&gt;go&lt;/code&gt; 是构建编排器，不是编译器&lt;/h2&gt;
&lt;p&gt;把一个最小程序的构建过程摊开，最直接的办法是 &lt;code&gt;go build -x&lt;/code&gt;，它会打印出 &lt;code&gt;go&lt;/code&gt; 实际执行的每一条
子命令。对一个只有一行 &lt;code&gt;fmt.Println&lt;/code&gt; 的 &lt;code&gt;main&lt;/code&gt; 包，输出的骨架是这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;15
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;16
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;17
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;18
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;19
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go build -x -o /dev/null .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WORK=/tmp/go-build1449454186
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p $WORK/b001/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat &amp;gt;$WORK/b001/importcfg &lt;span style="color:#a31515"&gt;&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39; # internal
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;# import config
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;packagefile fmt=/Users/.../go-build/7d/7d74...-d
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;packagefile runtime=/Users/.../go-build/60/60cb...-d
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;# 调用编译器，把 main.go 编成归档 _pkg_.a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.../pkg/tool/darwin_arm64/compile -o $WORK/b001/_pkg_.a -p main -lang=go1.26 &lt;span style="color:#a31515"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -complete -buildid ... -importcfg $WORK/b001/importcfg -pack ./main.go
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.../pkg/tool/darwin_arm64/buildid -w $WORK/b001/_pkg_.a &lt;span style="color:#008000"&gt;# internal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cp $WORK/b001/_pkg_.a /Users/.../go-build/82/8256...-d &lt;span style="color:#008000"&gt;# internal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;# 写出链接配置，再调用链接器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat &amp;gt;$WORK/b001/importcfg.link &lt;span style="color:#a31515"&gt;&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39; # internal
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;packagefile demo=$WORK/b001/_pkg_.a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;packagefile fmt=/Users/.../go-build/7d/7d74...-d
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a31515"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.../pkg/tool/darwin_arm64/link -o $WORK/b001/exe/a.out -importcfg ... $WORK/b001/_pkg_.a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;go&lt;/code&gt; 自己做的事，是建一个临时工作目录、写出一份份 &lt;code&gt;importcfg&lt;/code&gt;（告诉编译器与链接器每个依赖包的
归档在磁盘何处），然后 &lt;code&gt;exec&lt;/code&gt; 出 &lt;code&gt;compile&lt;/code&gt; 与 &lt;code&gt;link&lt;/code&gt; 这两个独立的可执行文件。源文件到目标代码
的真正翻译，发生在 &lt;code&gt;go&lt;/code&gt; 之外的工具里。这种「主程序只负责编排、把脏活交给独立工具」的结构，在
&lt;code&gt;go&lt;/code&gt; 命令的内部用两个数据类型承载：&lt;code&gt;Builder&lt;/code&gt; 持有整次构建的共享状态（工作目录、各类缓存、并行度
控制），&lt;code&gt;Action&lt;/code&gt; 则是构建图上的一个节点，描述「一道要做的工序」：&lt;/p&gt;</description></item><item><title>3.2 Go 程序编译流程</title><link>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/compile/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/compile/</guid><description>&lt;h1 id="32-go-程序编译流程"&gt;3.2 Go 程序编译流程&lt;/h1&gt;
&lt;p&gt;&lt;a href=".././cmd"&gt;3.1&lt;/a&gt; 说 &lt;code&gt;go build&lt;/code&gt; 背后真正干活的是 &lt;code&gt;compile&lt;/code&gt; 与 &lt;code&gt;link&lt;/code&gt; 两支程序。这一节把镜头推近到
&lt;code&gt;compile&lt;/code&gt;，给出它从一份 &lt;code&gt;.go&lt;/code&gt; 源文件到一个 &lt;code&gt;.o&lt;/code&gt; 目标文件之间走过的全程：每个阶段拿到什么、
吐出什么、为什么这样切分。本节是编译流水线的&lt;strong&gt;全景图&lt;/strong&gt;，每个阶段的内部机理（文法如何设计、
类型检查用什么算法、SSA 的优化规则）留给 &lt;a href="../../../part5toolchain/ch15compile/readme"&gt;第 15 章&lt;/a&gt;
逐一展开，这里只负责把它们串成一条线，并指出两条贯穿始终的暗线。&lt;/p&gt;
&lt;p&gt;为避免落回逐字翻译编译器目录结构的窠臼，下文用&lt;strong&gt;一个贯穿全程的小例子&lt;/strong&gt;来串起所有阶段。
请记住这段函数，它会被流水线反复揉捏，直到变成机器码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;func&lt;/span&gt; send(ch &lt;span style="color:#00f"&gt;chan&lt;/span&gt; &lt;span style="color:#2b91af"&gt;int&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#00f"&gt;go&lt;/span&gt; work() &lt;span style="color:#008000"&gt;// 启动一个新 goroutine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	ch &amp;lt;- 1 &lt;span style="color:#008000"&gt;// 向 channel 发送&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;它只有两行，却恰好藏着本节要强调的第二条暗线：&lt;code&gt;go&lt;/code&gt; 与 &lt;code&gt;&amp;lt;-&lt;/code&gt; 这样的语言关键字，最终都不是
靠 CPU 指令直接实现的，而是被编译器&lt;strong&gt;翻译成对运行时的函数调用&lt;/strong&gt;。先把流水线看完，再回头
解剖这两行。&lt;/p&gt;
&lt;h2 id="321-两条暗线为何要先讲它们"&gt;3.2.1 两条暗线：为何要先讲它们&lt;/h2&gt;
&lt;p&gt;把流水线的细节铺开之前，先点明两个贯穿全程的设计取向，后面每个阶段都在为它们服务。&lt;/p&gt;
&lt;p&gt;第一条暗线是&lt;strong&gt;编译速度本身是一等约束&lt;/strong&gt;。Go 从设计之初就把「编译要快」当作语言目标而非事后
优化（&lt;a href="../../ch01intro/history"&gt;1.1&lt;/a&gt;）。这个目标渗进了语法、依赖管理与编译器结构的每一层：
文法被设计成可以不回溯地快速解析，依赖以编译期产出的&lt;strong&gt;紧凑导出数据&lt;/strong&gt;而非源码传递，且不存在
C/C++ 那种会层层传染的 &lt;code&gt;#include&lt;/code&gt;。Rob Pike 在《Go at Google》里把这一点列为 Go 诞生的直接
动机，当年 Google 一次大型 C++ 构建动辄以小时计，而 Go 要做到「按一下回车就编译完」。&lt;/p&gt;</description></item><item><title>3.3 语言的自举</title><link>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/bootstrap/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/bootstrap/</guid><description>&lt;h1 id="33-语言的自举"&gt;3.3 语言的自举&lt;/h1&gt;
&lt;p&gt;一个问题听起来像悖论：Go 的编译器、汇编器、链接器与运行时如今都用 Go 写成，那么第一个能编译
Go 的程序是从哪里来的？没有 Go 编译器，怎么编译出 Go 编译器？这就是&lt;strong&gt;自举&lt;/strong&gt;（bootstrapping）。
它既是「先有鸡还是先有蛋」的经典困局，也是 Go 工具链演化史上一段可被精确复述的工程。本节
回答三件事：这只蛋最初是怎么孵出来的；自举之后，构建一个新版 Go 的链条如何运转，以及它对
引导版本的要求为何逐年抬升；最后，为什么一门语言「用自己实现自己」值得郑重对待。&lt;/p&gt;
&lt;h2 id="331-先有蛋从-c-写的工具链到-go-写的工具链"&gt;3.3.1 先有蛋：从 C 写的工具链到 Go 写的工具链&lt;/h2&gt;
&lt;p&gt;自举的前提，是先有一个不依赖自身的起点。Go 工具链最初（到 &lt;strong&gt;Go 1.4&lt;/strong&gt;，2014 年）的编译器、
汇编器、链接器与 &lt;code&gt;cmd/dist&lt;/code&gt; 都是用 &lt;strong&gt;C&lt;/strong&gt; 写的，编译器沿用了 Plan 9 的命名（&lt;code&gt;5c&lt;/code&gt;、&lt;code&gt;6c&lt;/code&gt;、&lt;code&gt;8c&lt;/code&gt;、
&lt;code&gt;9c&lt;/code&gt; 等，数字对应不同的目标架构）。这套 C 程序由系统自带的 &lt;code&gt;gcc&lt;/code&gt; 或 &lt;code&gt;clang&lt;/code&gt; 编译，于是构建
Go 不需要任何已存在的 Go,这便是那只「蛋」。&lt;/p&gt;
&lt;p&gt;从 &lt;strong&gt;Go 1.5（2015 年 8 月）&lt;strong&gt;起，Go 完成了一次里程碑式的转变：编译器与运行时被&lt;/strong&gt;整体用 Go
重写&lt;/strong&gt;，C 工具链被删除，工具链从此可以自我引导。这步棋的代价与收益都很实在。收益是，编译器
开发者从此用 Go 这门内存安全、并发友好、自带测试与剖析工具的语言来维护工具链，编译器里的
bug 可以用 Go 自己的调试手段去查；运行时与编译器共用一套语言，跨边界的协作（如逃逸分析与
栈管理）也更顺。代价是，构建 Go 不再像编译一段 C 那样自给自足，它现在需要「另一个已经能用的
Go」。如何用一个旧的 Go 造出一个新的 Go，就成了此后每个版本都要回答的问题。&lt;/p&gt;</description></item><item><title>3.4 模块链接</title><link>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/link/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/link/</guid><description>&lt;h1 id="34-模块链接"&gt;3.4 模块链接&lt;/h1&gt;
&lt;p&gt;编译器（&lt;a href=".././compile"&gt;3.2&lt;/a&gt;）把每个包变成一个目标文件，但目标文件还不能直接运行。它们彼此
引用着对方的函数与变量，地址尚未确定，还缺运行时的支撑。把这些碎片拼成一个完整、可加载执行
的程序，是&lt;strong&gt;链接器&lt;/strong&gt;（&lt;code&gt;cmd/link&lt;/code&gt;，通常以 &lt;code&gt;go tool link&lt;/code&gt; 的形式被 &lt;code&gt;go build&lt;/code&gt; 调起）的活。这一节
看链接器做了哪几件事，以及 Go 在链接上的几处独特选择,它们一同解释了为什么一个 Go 程序往往是
一个「拷过去就能跑」的自包含文件。&lt;/p&gt;
&lt;h2 id="341-链接器做的事"&gt;3.4.1 链接器做的事&lt;/h2&gt;
&lt;p&gt;链接器的输入是一个 main 包的目标文件，加上它递归依赖的所有包（含整个运行时）的目标文件；
输出是一个可被操作系统加载执行的二进制。中间它要顺序完成几件关键工作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;符号解析&lt;/strong&gt;（symbol resolution）：把每一处对外部符号的&lt;strong&gt;引用&lt;/strong&gt;，绑定到它在某个目标文件里的
唯一&lt;strong&gt;定义&lt;/strong&gt;。一个符号就是一个有名字的地址,某个函数的入口、某个全局变量的存储。A 包里写
&lt;code&gt;fmt.Println(...)&lt;/code&gt;，编译 A 时并不知道 &lt;code&gt;fmt.Println&lt;/code&gt; 在哪，只留下一个待解析的引用；链接器把它
接到 &lt;code&gt;fmt&lt;/code&gt; 包目标文件里那份定义上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;布局&lt;/strong&gt;（layout）：把所有符号的机器码与数据，按种类安排进可执行文件的各个段,代码进 text、
可读写数据进 data、只读数据（字符串字面量、类型信息）进 rodata 等。布局一旦定下，每个符号的
最终地址也就确定了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重定位&lt;/strong&gt;（relocation）：地址定下后，回头修正所有引用处填的临时地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;死代码消除&lt;/strong&gt;（dead-code elimination）：从入口出发不可达的函数与变量，根本不写进最终二进制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后产出的，是一个静态布局完毕、地址全部填实的文件。下面三节分别细看其中最值得说的三处：
重定位（3.4.2）、死代码消除（3.4.3）、以及「运行时也被一并链接进来」这一 Go 的标志性后果
（3.4.4）。&lt;/p&gt;

&lt;script src="http://golang.design/under-the-hood/mermaid.min.js"&gt;&lt;/script&gt;
&lt;script&gt;
 window.addEventListener("DOMContentLoaded", function () {
 mermaid.initialize({
 startOnLoad: false,
 theme: "neutral",
 securityLevel: "strict",
 fontFamily: "inherit",
 });
 mermaid.run({ querySelector: ".mermaid" });
 });
&lt;/script&gt;


&lt;pre class="mermaid"&gt;flowchart LR
 OBJ[&amp;#34;main.a &amp;#43; 依赖包目标文件&amp;lt;br/&amp;gt;(含整个 runtime)&amp;#34;] --&amp;gt; LOAD[&amp;#34;装载符号&amp;lt;br/&amp;gt;loader 读入&amp;#34;]
 LOAD --&amp;gt; RESOLVE[&amp;#34;符号解析&amp;lt;br/&amp;gt;引用 → 定义&amp;#34;]
 RESOLVE --&amp;gt; DEAD[&amp;#34;死代码消除&amp;lt;br/&amp;gt;从 main.main 标记可达&amp;#34;]
 DEAD --&amp;gt; LAYOUT[&amp;#34;段布局&amp;lt;br/&amp;gt;text/data/rodata&amp;#34;]
 LAYOUT --&amp;gt; RELOC[&amp;#34;重定位&amp;lt;br/&amp;gt;填实地址&amp;#34;]
 RELOC --&amp;gt; BIN[&amp;#34;可执行二进制&amp;#34;]&lt;/pre&gt;
&lt;h2 id="342-符号解析与重定位"&gt;3.4.2 符号解析与重定位&lt;/h2&gt;
&lt;p&gt;这两步是链接的核心机制，值得稍微形式化一点。&lt;/p&gt;</description></item><item><title>3.5 Go 程序启动引导</title><link>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/boot/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/boot/</guid><description>&lt;h1 id="35-go-程序启动引导"&gt;3.5 Go 程序启动引导&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;本节内容对标 Go 1.26。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;读者写下的 &lt;code&gt;main&lt;/code&gt; 函数，并不是程序真正的第一条指令。当操作系统把控制权交给一个 Go
可执行文件时，跑起来的是运行时（runtime）：它要在主线程上铺好执行栈、绑定线程本地存储、
量出 CPU 核心数与内存物理页大小，把内存分配器、垃圾回收器、调度器逐一唤醒，最后才创建那个
承载 &lt;code&gt;main&lt;/code&gt; 的 goroutine，交由调度循环去运行。换言之，一个 Go 二进制文件里随身带着一个
微型操作系统（&lt;a href="../../../ch01intro/go"&gt;1.2&lt;/a&gt;），它先于用户代码自行启动。本节顺着这条引导链走一遍，
从操作系统的入口符号，到第一个 goroutine 被调度，看清楚「在 &lt;code&gt;main&lt;/code&gt; 之前」究竟发生了什么。&lt;/p&gt;
&lt;p&gt;引导链的脉络可以先记住三段：汇编入口在主线程上手工搭出 &lt;code&gt;g0&lt;/code&gt; 与 &lt;code&gt;m0&lt;/code&gt;（&lt;a href="#351-%E6%B1%87%E7%BC%96%E5%85%A5%E5%8F%A3g0m0-%E4%B8%8E-tls"&gt;3.5.1&lt;/a&gt;），
&lt;code&gt;schedinit&lt;/code&gt; 按依赖次序点亮各个子系统（&lt;a href="#352-schedinit%E6%8C%89%E6%AC%A1%E5%BA%8F%E5%94%A4%E9%86%92%E8%BF%90%E8%A1%8C%E6%97%B6"&gt;3.5.2&lt;/a&gt;），随后
&lt;code&gt;newproc&lt;/code&gt; 造出第一个 goroutine、&lt;code&gt;mstart&lt;/code&gt; 进入调度循环把它跑起来（&lt;a href="#353-%E7%AC%AC%E4%B8%80%E4%B8%AA-goroutine-%E4%B8%8E%E8%B0%83%E5%BA%A6%E5%BE%AA%E7%8E%AF"&gt;3.5.3&lt;/a&gt;）。&lt;/p&gt;
&lt;h2 id="351-汇编入口g0m0-与-tls"&gt;3.5.1 汇编入口：g0、m0 与 TLS&lt;/h2&gt;
&lt;p&gt;运行时的真正入口由 runtime 包以汇编写就。以 AMD64 为例，Linux 与 macOS 的入口符号分别位于
&lt;code&gt;runtime/rt0_linux_amd64.s&lt;/code&gt; 与 &lt;code&gt;runtime/rt0_darwin_amd64.s&lt;/code&gt;，两者都只是一跳，转入共用的
&lt;code&gt;_rt0_amd64&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-asm" data-lang="asm"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	JMP	_rt0_amd64(SB)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	JMP	_rt0_amd64(SB)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;为何按「架构 + 操作系统」两维拆分入口？因为程序编译为机器码后，指令集只取决于 CPU 架构，
而操作系统的差异体现在系统调用等系统级操作上。一份 &lt;code&gt;_rt0_amd64&lt;/code&gt; 可被多个操作系统共用，
各操作系统再以自己的入口符号跳进来。&lt;code&gt;rt0&lt;/code&gt; 是 &lt;code&gt;runtime0&lt;/code&gt; 的缩写，意指运行时的创生，
此后所有派生出来的同类对象都以 &lt;code&gt;1&lt;/code&gt; 为后缀，&lt;code&gt;g0&lt;/code&gt;、&lt;code&gt;m0&lt;/code&gt; 即得名于此。&lt;/p&gt;</description></item><item><title>3.6 主 Goroutine 的生与死</title><link>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/main/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>http://golang.design/under-the-hood/zh-cn/part1overview/ch03life/main/</guid><description>&lt;h1 id="36-主-goroutine-的生与死"&gt;3.6 主 Goroutine 的生与死&lt;/h1&gt;
&lt;p&gt;&lt;a href=".././init"&gt;3.5&lt;/a&gt; 里 &lt;code&gt;schedinit&lt;/code&gt; 把运行时的家底备齐之后并不直接调用 &lt;code&gt;runtime.main&lt;/code&gt;，而是把它
的入口地址压栈、交给 &lt;code&gt;newproc&lt;/code&gt; 造出第一个 Goroutine，再由 &lt;code&gt;mstart&lt;/code&gt; 启动调度循环、把这个
Goroutine 挑出来执行。调度的细节留到 &lt;a href="../../part3concurrency/ch09sched"&gt;9 调度器&lt;/a&gt; 详谈，本节
只把镜头对准一个时刻：&lt;strong&gt;第一个 Goroutine 已经在跑，它正要执行 &lt;code&gt;runtime.main&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这只 Goroutine 与众不同。它是程序里诞生的第一个，承载着用户的 &lt;code&gt;main.main&lt;/code&gt;，也独自掌握着整个
进程的生杀大权：它一返回，进程就结束，哪怕别处还有上千个 Goroutine 正忙。理解它做了什么、
以及它何时收手，是理解「一个 Go 程序如何开始、又如何整体退出」的最后一块拼图。&lt;/p&gt;
&lt;h2 id="361-runtimemain用户代码之前的铺垫"&gt;3.6.1 &lt;code&gt;runtime.main&lt;/code&gt;：用户代码之前的铺垫&lt;/h2&gt;
&lt;p&gt;运行时包里的 &lt;code&gt;main&lt;/code&gt; 函数（即 &lt;code&gt;runtime.main&lt;/code&gt;）与用户的 &lt;code&gt;main.main&lt;/code&gt; 跑在同一个 Goroutine 上，
但在把控制权交给用户之前，它先料理了几桩只能由「第一个 Goroutine」来做的事。下面是裁剪后的
速写，只留与生命周期相关的骨架：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;15
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;16
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;17
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;18
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;19
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;20
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;21
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;22
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;23
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;24
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;25
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;26
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;27
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;28
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;29
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;30
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;31
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;32
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;33
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;34
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;35
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;36
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;37
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;38
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;39
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;40
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;41
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;42
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;43
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;44
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;45
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;46
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#008000"&gt;// 主 Goroutine 入口（裁剪自 runtime/proc.go）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#00f"&gt;func&lt;/span&gt; main() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	mp := getg().m
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#008000"&gt;// 执行栈上限：64 位 1GB，32 位 250MB（用十进制，崩溃信息里更好看）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#00f"&gt;if&lt;/span&gt; goarch.PtrSize == 8 {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		maxstacksize = 1000000000
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	} &lt;span style="color:#00f"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		maxstacksize = 250000000
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	mainStarted = &lt;span style="color:#00f"&gt;true&lt;/span&gt; &lt;span style="color:#008000"&gt;// 允许 newproc 启动新的 M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#00f"&gt;if&lt;/span&gt; haveSysmon { &lt;span style="color:#008000"&gt;// 启动系统监控线程（见 9.8）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		systemstack(&lt;span style="color:#00f"&gt;func&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			newm(sysmon, &lt;span style="color:#00f"&gt;nil&lt;/span&gt;, -1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	lockOSThread() &lt;span style="color:#008000"&gt;// 初始化期间把主 G 锁在主 OS 线程上&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#00f"&gt;if&lt;/span&gt; mp != &amp;amp;m0 {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		throw(&lt;span style="color:#a31515"&gt;&amp;#34;runtime.main not on m0&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	runtimeInitTime = nanotime() &lt;span style="color:#008000"&gt;// 记下「世界开始」的时刻，须在 doInit 之前&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	doInit(runtime_inittasks) &lt;span style="color:#008000"&gt;// 运行运行时自身的 init（含 GC、defer 类型等）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	gcenable() &lt;span style="color:#008000"&gt;// 启用垃圾回收器（见 13）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#008000"&gt;// 按依赖顺序运行所有模块（含用户包）的 init 任务&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	last := lastmoduledatap
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#00f"&gt;for&lt;/span&gt; m := &amp;amp;firstmoduledata; &lt;span style="color:#00f"&gt;true&lt;/span&gt;; m = m.next {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		doInit(m.inittasks)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#00f"&gt;if&lt;/span&gt; m == last {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#00f"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	unlockOSThread()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	fn := main_main &lt;span style="color:#008000"&gt;// 间接调用：链接器此时不知道 main 包的地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	fn()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	exit(0) &lt;span style="color:#008000"&gt;// 主 G 一返回，整个进程退出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这段骨架里有几处值得停下来看。&lt;/p&gt;</description></item></channel></rss>