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