<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Runtime on 我的技术博客</title><link>https://buvidk1234.github.io/tags/runtime/</link><description>Recent content in Runtime on 我的技术博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 18 Aug 2026 00:19:00 +0800</lastBuildDate><atom:link href="https://buvidk1234.github.io/tags/runtime/index.xml" rel="self" type="application/rss+xml"/><item><title>Go Runtime（十）：Timer实现</title><link>https://buvidk1234.github.io/posts/go-runtime-10-timer/</link><pubDate>Tue, 18 Aug 2026 00:19:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-10-timer/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。Go 1.23 重写了 Timer Channel 的可回收性与 Stop/Reset 保证；阅读旧版本分析时必须先确认版本边界。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Timer 不是一个独立后台 goroutine 加一张全局表。Go 1.26 把真实时间 Timer 组织在每个 P 的四叉最小堆中，由调度器和 netpoll 共同决定睡到何时、何时执行到期回调。&lt;/p&gt;
&lt;p&gt;它同时服务 &lt;code&gt;time.Sleep&lt;/code&gt;、&lt;code&gt;time.Timer&lt;/code&gt;、&lt;code&gt;time.Ticker&lt;/code&gt;、&lt;code&gt;AfterFunc&lt;/code&gt; 和网络 Deadline。难点不只是“按时间排序”，而是并发 Stop/Reset、跨 P 修改、周期跳期、Channel 旧值以及 GC 可达性。&lt;/p&gt;
&lt;h2 id="time包如何进入runtime"&gt;time包如何进入runtime&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;time&lt;/code&gt; 包通过 runtime 提供的私有入口创建和修改 Timer：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;time.Sleep -&amp;gt; runtime.timeSleep
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;time.NewTimer -&amp;gt; runtime.newTimer(sendTime, channel)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Timer.Stop -&amp;gt; runtime.stopTimer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Timer.Reset -&amp;gt; runtime.resetTimer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;time.AfterFunc -&amp;gt; runtime.newTimer(goFunc, nil)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;net deadline -&amp;gt; runtime timer callback&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;&lt;a href="https://github.com/golang/go/blob/go1.26.2/src/time/sleep.go#L143"&gt;&lt;code&gt;time.NewTimer&lt;/code&gt;&lt;/a&gt; 的源码仍写 &lt;code&gt;make(chan Time, 1)&lt;/code&gt;。默认模式下 runtime 借助 &lt;code&gt;hchan.timer&lt;/code&gt; 对外呈现同步、容量为零的语义；设置 &lt;code&gt;GODEBUG=asynctimerchan=1&lt;/code&gt; 才恢复 Go 1.23 以前的异步 Timer Channel 行为。因此不能只看 &lt;code&gt;make&lt;/code&gt; 参数推断 &lt;code&gt;cap(t.C)&lt;/code&gt; 和 Stop/Reset 语义。&lt;/p&gt;</description></item><item><title>Go Runtime（九）：Runtime信号量与同步原语</title><link>https://buvidk1234.github.io/posts/go-runtime-09-sync-primitives/</link><pubDate>Tue, 18 Aug 2026 00:18:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-09-sync-primitives/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。&lt;code&gt;sync&lt;/code&gt; 的公开语义由标准库保证；本文讨论的状态位、队列和 runtime 接口属于版本相关实现，不应被业务代码依赖。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;sync.Mutex&lt;/code&gt; 的竞争慢路径、&lt;code&gt;RWMutex&lt;/code&gt; 的读写协调和 &lt;code&gt;WaitGroup&lt;/code&gt; 的等待，最终都需要把 G 停下来并在条件满足时重新放回调度器。它们共享 runtime semaphore，但并不共享完全相同的高层状态机。&lt;/p&gt;
&lt;p&gt;先建立边界：runtime semaphore 是“按地址等待与唤醒”的底层设施，不是公开的计数信号量类型。存放在 &lt;code&gt;uint32&lt;/code&gt; 地址中的 token 只服务于防丢唤醒和交接协议。&lt;/p&gt;
&lt;h2 id="从sync包到runtime"&gt;从sync包到runtime&lt;/h2&gt;
&lt;p&gt;标准库通过无函数体声明和 &lt;code&gt;go:linkname&lt;/code&gt; 连接 runtime：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;internal/sync
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Mutex slow path
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; runtime_SemacquireMutex
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; runtime_Semrelease
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sync
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; RWMutex -&amp;gt; readerSem / writerSem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; WaitGroup -&amp;gt; sema
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Cond -&amp;gt; notifyList&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;公开类型保留自己的原子状态；runtime 只负责等待队列、park/ready 和 contention profile。把所有语义都归因于 semaphore，会看不懂 Mutex 的 starvation、RWMutex 的 writer preference 和 WaitGroup 的 misuse 检测。&lt;/p&gt;</description></item><item><title>Go Runtime（八）：Channel与Select</title><link>https://buvidk1234.github.io/posts/go-runtime-08-channel-select/</link><pubDate>Tue, 18 Aug 2026 00:17:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-08-channel-select/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。Channel 的语言语义跨平台一致；文中的字段、函数和行号固定到该版本，后续版本可能调整实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Channel 不是“带锁队列”的同义词。它同时实现值传递、goroutine 阻塞与唤醒、&lt;code&gt;close&lt;/code&gt; 广播、&lt;code&gt;select&lt;/code&gt; 多路竞争，并且要和栈复制、GC、race detector、Timer 配合。&lt;/p&gt;
&lt;p&gt;理解 Channel 的关键，是区分三种正常数据路径：直接交接、环形缓冲区、等待队列。前两条可以立即完成；进入等待队列才会把当前 G 变为 &lt;code&gt;_Gwaiting&lt;/code&gt;。nil Channel 的阻塞操作是没有数据路径的永久 park，属于另一个边界情况。&lt;/p&gt;
&lt;h2 id="从语法到runtime入口"&gt;从语法到runtime入口&lt;/h2&gt;
&lt;p&gt;编译器把普通发送和接收分别降低为 &lt;code&gt;runtime.chansend1&lt;/code&gt;、&lt;code&gt;runtime.chanrecv1/2&lt;/code&gt;。非阻塞的单 case &lt;code&gt;select&lt;/code&gt; 会被优化为 &lt;code&gt;selectnbsend&lt;/code&gt; 或 &lt;code&gt;selectnbrecv&lt;/code&gt;；多个有效 case 才构造 case 数组并调用 &lt;code&gt;runtime.selectgo&lt;/code&gt;。&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;GO&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// runtime.chansend1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// runtime.chanrecv1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// runtime.chanrecv2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// runtime.closechan&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;这意味着 &lt;code&gt;select&lt;/code&gt; 不是对每个 case 依次执行一次普通 channel 操作。它必须在所有相关 Channel 锁的保护下完成一次原子决策。&lt;/p&gt;
&lt;p&gt;源码入口：&lt;a href="https://github.com/golang/go/blob/go1.26.2/src/cmd/compile/internal/walk/select.go#L56"&gt;&lt;code&gt;walk/select.go&lt;/code&gt;&lt;/a&gt; 与 &lt;a href="https://github.com/golang/go/blob/go1.26.2/src/runtime/chan.go#L159"&gt;&lt;code&gt;chan.go&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="hchan保存什么"&gt;hchan保存什么&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/golang/go/blob/go1.26.2/src/runtime/chan.go#L34"&gt;&lt;code&gt;hchan&lt;/code&gt;&lt;/a&gt; 是 Channel 的运行时对象：&lt;/p&gt;</description></item><item><title>Go Runtime（七）：Signal与cgo线程管理</title><link>https://buvidk1234.github.io/posts/go-runtime-07-signal-cgo/</link><pubDate>Tue, 18 Aug 2026 00:16:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-07-signal-cgo/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。Signal 的编号、trampoline 和上下文结构具有平台差异；本文只把 Unix/Linux 主线作为具体实现，Windows 异常和 IOCP 回调需要单独对照平台文件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;信号和 cgo 都会打破普通 Go 调用链的假设：信号可以在任意指令附近异步到达，C 代码可能阻塞、修改线程状态，甚至从一个非 Go 创建的线程回调 Go。&lt;/p&gt;
&lt;p&gt;runtime 必须在这些边界上重新建立可识别的线程、栈和执行现场，同时避免在不安全上下文中执行分配、加锁或调度操作。&lt;/p&gt;
&lt;h2 id="signal承担哪些职责"&gt;Signal承担哪些职责&lt;/h2&gt;
&lt;p&gt;Go runtime 使用或处理信号的目的并不相同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把 &lt;code&gt;SIGINT&lt;/code&gt;、&lt;code&gt;SIGTERM&lt;/code&gt; 等转交给 &lt;code&gt;os/signal&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;将某些同步 fault 转换为 Go panic。&lt;/li&gt;
&lt;li&gt;用异步信号抢占长时间运行的 goroutine。&lt;/li&gt;
&lt;li&gt;采样 &lt;code&gt;SIGPROF&lt;/code&gt; 生成 CPU profile。&lt;/li&gt;
&lt;li&gt;在 cgo 场景与 C 安装的 handler 协作。&lt;/li&gt;
&lt;li&gt;对致命信号打印 traceback 并退出。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此不能笼统地说“Go 捕获了信号”。同一个 handler 入口会根据 signal 类型、发生线程和当前 G/M 状态走不同分支。&lt;/p&gt;
&lt;h2 id="sigtable描述策略"&gt;sigtable描述策略&lt;/h2&gt;
&lt;p&gt;runtime 为平台信号维护一张策略表，描述信号的默认行为和处理属性。概念上包括：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;是否通知用户层
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;是否默认忽略
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;是否导致 panic
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;是否打印 stack
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;是否导致进程退出
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;是否属于同步 fault&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;策略还会受程序是否导入 &lt;code&gt;os/signal&lt;/code&gt;、是否使用 cgo、signal disposition 是否被外部代码修改等因素影响。&lt;/p&gt;</description></item><item><title>Go Runtime（六）：Netpoll与Syscall</title><link>https://buvidk1234.github.io/posts/go-runtime-06-netpoll-syscall/</link><pubDate>Tue, 18 Aug 2026 00:15:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-06-netpoll-syscall/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。尤其注意：Go 1.26.2 已不再使用历史上的 &lt;code&gt;_Psyscall&lt;/code&gt; 状态；旧文章基于 &lt;code&gt;_Psyscall&lt;/code&gt; 描述的状态机不能直接套用到当前源码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;网络等待和阻塞系统调用都会让当前 goroutine 暂时无法继续，但 runtime 的处理方式不同：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;非阻塞 fd + netpoll
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; G 等在 runtime 中，M/P 可以继续调度
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;阻塞 syscall
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; M 等在内核中，runtime 设法把 P 交给其他 M&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;理解这一区别，就能解释 Go 为什么能用同步风格编写高并发网络服务，以及为什么某些 syscall 仍会增加线程数量。&lt;/p&gt;
&lt;h2 id="从netconnread到runtime"&gt;从net.Conn.Read到runtime&lt;/h2&gt;
&lt;p&gt;以 Unix 上 TCP 读取为例，调用链可简化为：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;net.(*TCPConn).Read
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; netFD.Read
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; internal/poll.(*FD).Read
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; syscall.Read
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; 数据就绪：直接返回
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; EAGAIN：pollDesc.waitRead
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; runtime_pollWait
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; runtime.netpollblock
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -&amp;gt; gopark&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;socket 被设置为非阻塞。第一次 &lt;code&gt;read&lt;/code&gt; 仍然是真实系统调用；只有内核返回 &lt;code&gt;EAGAIN&lt;/code&gt;/&lt;code&gt;EWOULDBLOCK&lt;/code&gt; 时，当前 G 才进入 netpoll 等待。&lt;/p&gt;</description></item><item><title>Go Runtime（五）：GC、写屏障与Pacer</title><link>https://buvidk1234.github.io/posts/go-runtime-05-gc/</link><pubDate>Tue, 18 Aug 2026 00:14:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-05-gc/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64，默认 GOEXPERIMENT&lt;/strong&gt;。Go 1.26.2 源码包含 build-tag 隔离的 Green Tea GC 实现，但默认 &lt;code&gt;GOEXPERIMENT&lt;/code&gt; 下 &lt;code&gt;goexperiment.GreenTeaGC == false&lt;/code&gt;；本文主线分析默认标记器，不把实验实现混入结论。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go GC 的核心不是“三色”这个名词，而是在用户 goroutine 持续修改对象图时，怎样并发找出仍然可达的对象，并把暂停时间与 CPU、内存开销控制在目标范围内。&lt;/p&gt;
&lt;p&gt;一轮 GC 可以理解为四套机制协作：标记算法判断对象生死，写屏障维护并发正确性，分配 goroutine 通过 assist 偿还标记债务，pacer 控制何时开始以及投入多少资源。&lt;/p&gt;
&lt;h2 id="为什么需要gc"&gt;为什么需要GC&lt;/h2&gt;
&lt;p&gt;函数返回只能自然回收栈帧，无法决定堆对象是否仍被其他对象引用：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;GO&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;newNode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;对象可能通过全局变量、其他堆对象、goroutine 栈或 runtime 结构继续可达。GC 必须从一组根出发遍历引用图，未被发现的堆对象才可以回收。&lt;/p&gt;
&lt;h2 id="根对象"&gt;根对象&lt;/h2&gt;
&lt;p&gt;典型根包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全局变量和包级数据中的指针。&lt;/li&gt;
&lt;li&gt;各 goroutine 活动栈帧中的指针。&lt;/li&gt;
&lt;li&gt;寄存器或保存执行现场中的指针。&lt;/li&gt;
&lt;li&gt;runtime 持有的特定对象引用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;编译器生成的类型位图和栈图告诉 GC 哪些位置是指针。Go 的标记器不会把任意看起来像地址的整数都当作指针，这提高了准确性，也要求 syscall、汇编和 cgo 遵守指针与栈管理规则。&lt;/p&gt;</description></item><item><title>Go Runtime（四）：内存分配器</title><link>https://buvidk1234.github.io/posts/go-runtime-04-allocator/</link><pubDate>Tue, 18 Aug 2026 00:13:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-04-allocator/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。文中的源码链接固定到 &lt;code&gt;go1.26.2&lt;/code&gt; tag；对象大小阈值、size class 表和局部快路径可能随版本变化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go 内存分配器要同时满足三类目标：小对象分配足够快，多 P 并发时减少共享锁竞争，GC 又能由任意堆地址快速定位对象边界和标记元数据。它采用按对象规格分级、按 P 缓存、按页统一管理的结构。&lt;/p&gt;
&lt;p&gt;对象是否进入这里首先由编译器逃逸分析决定；本文从已经确定需要堆分配的对象开始，追踪 &lt;code&gt;mallocgc&lt;/code&gt; 到 mcache、mcentral、mheap 和页分配器。&lt;/p&gt;
&lt;h2 id="先确认对象是否真的进入堆"&gt;先确认对象是否真的进入堆&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;new(T)&lt;/code&gt; 不等于堆分配，返回局部变量地址也不必机械地等于堆分配；编译器根据值是否可能在当前栈帧结束后继续可达、地址是否暴露给未知调用等条件做逃逸分析，随后内联和标量替换还可能消除分配。&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;GO&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alloc&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;//go:noinline&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;makeNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;BASH&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; -gcflags&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;all=-m=2&amp;#39;&lt;/span&gt; ./...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; -run&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^$&amp;#39;&lt;/span&gt; -bench&lt;span class="o"&gt;=&lt;/span&gt;. -benchmem ./...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;第一条解释编译器决定，第二条测量最终二进制的每次操作分配。只看源码语法或只看逃逸日志都不足以判断生产路径成本。&lt;/p&gt;
&lt;h2 id="分配器的层级"&gt;分配器的层级&lt;/h2&gt;
&lt;p&gt;Go 将小对象归入 size class，再按 spanClass 管理 span。spanClass 同时编码 size class 和对象是否为 noscan。&lt;/p&gt;</description></item><item><title>Go Runtime（三）：Goroutine栈管理</title><link>https://buvidk1234.github.io/posts/go-runtime-03-stack/</link><pubDate>Tue, 18 Aug 2026 00:12:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-03-stack/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。文中的源码链接固定到 &lt;code&gt;go1.26.2&lt;/code&gt; tag；其他版本和架构可能具有不同的汇编入口、常量或实现细节。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;goroutine 能够以较低成本大量创建，一个重要原因是它不需要预留传统线程栈那样大的连续空间。Go 为普通 G 使用可增长的连续栈：空间不足时分配更大的栈、复制仍在使用的部分、修正指针，再从原调用点继续执行。&lt;/p&gt;
&lt;p&gt;本文只讨论 goroutine 栈的运行时管理。对象为什么逃逸到堆由编译器决定，堆对象如何分配放在&lt;a href="https://buvidk1234.github.io/posts/go-runtime-04-allocator/"&gt;下一篇：内存分配器&lt;/a&gt;中。&lt;/p&gt;
&lt;h2 id="三类栈不要混淆"&gt;三类栈不要混淆&lt;/h2&gt;
&lt;p&gt;一个 M 至少关联三种执行上下文：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;上下文&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;是否普通调度G&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;用户 G 的栈&lt;/td&gt;
&lt;td&gt;执行用户 Go 函数，可增长和缩小&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m.g0&lt;/code&gt; 的系统栈&lt;/td&gt;
&lt;td&gt;调度、栈扩容、部分 runtime 内部路径&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m.gsignal&lt;/code&gt; 的信号栈&lt;/td&gt;
&lt;td&gt;Unix 信号处理&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;g0&lt;/code&gt; 和 &lt;code&gt;gsignal&lt;/code&gt; 都是 &lt;code&gt;g&lt;/code&gt;，但它们不是放进运行队列的普通 goroutine。&lt;code&gt;newstack&lt;/code&gt; 必须在 g0 上运行，因为当前用户栈已经被判定为不足，不能继续依赖它完成内存分配和现场搬迁。&lt;/p&gt;
&lt;h2 id="g中的栈字段"&gt;g中的栈字段&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/golang/go/blob/go1.26.2/src/runtime/runtime2.go#L473"&gt;&lt;code&gt;runtime.g&lt;/code&gt;&lt;/a&gt; 的栈相关字段是理解整条链路的入口：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;GO&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stackguard0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uintptr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stackguard1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uintptr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sched&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;gobuf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;syscallsp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uintptr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;syscallpc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uintptr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uintptr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uintptr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;&lt;code&gt;stack&lt;/code&gt; 描述实际区间 &lt;code&gt;[lo, hi)&lt;/code&gt;。Go 栈在 amd64 上向低地址增长，SP 越来越接近 &lt;code&gt;lo&lt;/code&gt;。&lt;code&gt;stackguard0&lt;/code&gt; 是普通 Go 函数入口用于比较的保护值；正常情况下约为 &lt;code&gt;stack.lo + stackGuard&lt;/code&gt;，也可以被设置成特殊值 &lt;code&gt;stackPreempt&lt;/code&gt;，借用同一套入口检查触发协作式抢占。&lt;/p&gt;</description></item><item><title>Go Runtime（二）：GMP与Goroutine调度</title><link>https://buvidk1234.github.io/posts/go-runtime-02-scheduler/</link><pubDate>Tue, 18 Aug 2026 00:11:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-02-scheduler/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。调度器变化频繁；函数名、队列容量和分支顺序均以固定版本源码为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go 调度器解决的核心问题是：怎样把大量 goroutine 映射到较少的 OS 线程，同时控制竞争、阻塞和调度延迟。&lt;/p&gt;
&lt;p&gt;理解调度器不需要记住所有函数。先抓住三件事：执行资格属于 P，等待发生在 G 上，真正运行机器指令的是 M。&lt;/p&gt;
&lt;h2 id="gmp模型"&gt;GMP模型&lt;/h2&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;G（goroutine）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 栈、入口函数、调度现场、状态、等待原因
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;M（machine）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; OS线程、当前G、g0、信号处理上下文
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P（processor）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 本地运行队列、runnext、mcache、timer、执行资格&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;抽象关系如下：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; P.local runq
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [G][G][G][G]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;M(OS thread) + P ----&amp;gt; 执行某个 G
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; P.runnext&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;M 必须绑定 P 才能执行普通 Go 代码。没有 P 的 M 可以停在线程阻塞、syscall、cgo 或 runtime 的特定路径中，但不能继续执行任意用户 Go 代码。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GOMAXPROCS&lt;/code&gt; 控制 P 的数量。它限制的是并行执行 Go 代码的能力，不限制 goroutine 或 OS 线程的总数。&lt;/p&gt;</description></item><item><title>Go Runtime（一）：运行时架构与程序启动</title><link>https://buvidk1234.github.io/posts/go-runtime-01-bootstrap/</link><pubDate>Tue, 18 Aug 2026 00:10:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-01-bootstrap/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。源码链接固定到 &lt;code&gt;go1.26.2&lt;/code&gt; tag；其他操作系统、架构和构建模式的入口汇编会不同。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go runtime 是链接进 Go 可执行文件的一组基础设施。它不解释字节码，也不需要作为独立进程启动；它和编译器、标准库及操作系统共同实现 Go 的语言语义和执行模型。&lt;/p&gt;
&lt;p&gt;本文先建立 runtime 的模块边界，再从 Linux/amd64 可执行文件入口追踪到 &lt;code&gt;main.main&lt;/code&gt;。后续文章分别沿调度、栈、分配、GC、Channel、同步、Timer 和系统集成深入。&lt;/p&gt;
&lt;h2 id="runtime解决什么问题"&gt;Runtime解决什么问题&lt;/h2&gt;
&lt;p&gt;一段普通 Go 程序表面上只包含函数调用、对象分配和 goroutine：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;GO&lt;/summary&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;但这些语法背后至少需要 runtime 完成以下工作：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;语法或行为&lt;/th&gt;
&lt;th&gt;Runtime职责&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;go f()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建 G、准备初始栈、加入可运行队列&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new(T)&lt;/code&gt;、&lt;code&gt;&amp;amp;T{}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;按对象大小和指针信息分配内存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;channel 收发&lt;/td&gt;
&lt;td&gt;匹配发送方与接收方，必要时阻塞或唤醒 G&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;栈空间不足&lt;/td&gt;
&lt;td&gt;分配更大连续栈，复制现场并修正栈内指针&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;堆对象失去引用&lt;/td&gt;
&lt;td&gt;并发标记、清扫和复用内存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;网络读写等待&lt;/td&gt;
&lt;td&gt;将 G 挂起到 netpoll，fd 就绪后恢复运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;panic/defer/recover&lt;/td&gt;
&lt;td&gt;展开调用栈并按语言规则执行延迟调用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;因此，runtime 不是一个单独功能，而是支撑程序启动、调度、内存、GC、同步和系统交互的一组机制。&lt;/p&gt;</description></item></channel></rss>