<?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>Stack on 我的技术博客</title><link>https://buvidk1234.github.io/tags/stack/</link><description>Recent content in Stack on 我的技术博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 18 Aug 2026 00:12:00 +0800</lastBuildDate><atom:link href="https://buvidk1234.github.io/tags/stack/index.xml" rel="self" type="application/rss+xml"/><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></channel></rss>