<?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>Zombie on 大飞的博客</title>
    <link>https://www.dafei.me/tags/zombie/</link>
    <description>Recent content in Zombie on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 13 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/zombie/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（二十）：wait / exit 完整语义</title>
      <link>https://www.dafei.me/posts/os-20-wait-exit/</link>
      <pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-20-wait-exit/</guid>
      <description>&lt;p&gt;上一章实现了 &lt;code&gt;exec()&lt;/code&gt;，用户程序能跑起来了。但进程退出时只是把进程表项清掉，父进程拿不到退出码，也没有办法等待。这一章实现完整的 &lt;code&gt;exit&lt;/code&gt; / &lt;code&gt;wait&lt;/code&gt; 语义。&lt;/p&gt;
&lt;h2 id=&#34;为什么进程退出后不能直接消失&#34;&gt;为什么进程退出后不能直接消失&lt;/h2&gt;
&lt;p&gt;直觉上，进程退出就应该立刻清掉进程表项。但这里有个问题：&lt;strong&gt;父进程怎么拿到子进程的退出码？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果进程表项直接清掉，退出码就丢了。父进程调用 &lt;code&gt;wait()&lt;/code&gt; 的时机可能比子进程 &lt;code&gt;exit()&lt;/code&gt; 晚，也可能早——无论哪种情况，父进程都需要能读到那个退出码。&lt;/p&gt;
&lt;p&gt;Linux 的解法是引入**僵尸（zombie）**状态：进程 &lt;code&gt;exit()&lt;/code&gt; 后不立即消失，保留退出码，等父进程 &lt;code&gt;wait()&lt;/code&gt; 读取后才彻底释放。&lt;/p&gt;
&lt;p&gt;这就是为什么 &lt;code&gt;ps&lt;/code&gt; 输出里偶尔会出现状态为 &lt;code&gt;Z&lt;/code&gt; 的进程——它已经退出了，只是在等父进程来&amp;quot;收尸&amp;quot;。&lt;/p&gt;
&lt;h2 id=&#34;进程状态机的变化&#34;&gt;进程状态机的变化&lt;/h2&gt;
&lt;p&gt;原来进程只有三个状态：UNUSED、READY、RUNNING。这一章加两个：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;UNUSED → READY → RUNNING
                    ↓ exit()
                 ZOMBIE   ← 已退出，等父进程 wait
                    ↑ 唤醒
                 BLOCKED  ← 父进程 wait 时挂起
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;调度器只挑 READY 的进程运行，ZOMBIE 和 BLOCKED 自然就不会被调度到，不需要改调度器的逻辑。&lt;/p&gt;
&lt;h2 id=&#34;exit-和-wait-的实现思路&#34;&gt;exit 和 wait 的实现思路&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;exit&lt;/strong&gt; 做三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把退出码存到进程表项，状态改为 ZOMBIE&lt;/li&gt;
&lt;li&gt;如果父进程正在 BLOCKED 等待，把它改回 READY（唤醒）&lt;/li&gt;
&lt;li&gt;切回内核栈和内核页表，&lt;code&gt;hlt&lt;/code&gt; 循环等待被回收&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;wait&lt;/strong&gt; 做的事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;扫描进程表，找有没有自己的子进程且状态是 ZOMBIE&lt;/li&gt;
&lt;li&gt;找到了：读退出码，把它设为 UNUSED，返回退出码&lt;/li&gt;
&lt;li&gt;没找到但有活着的子进程：挂起等待（ring3）或返回 -2 让调用方重试（ring0）&lt;/li&gt;
&lt;li&gt;没有子进程：返回 -1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;parent_pid&lt;/code&gt; 需要在 &lt;code&gt;exec&lt;/code&gt; / &lt;code&gt;fork&lt;/code&gt; 时记下来，这样 &lt;code&gt;exit&lt;/code&gt; 才知道该唤醒谁，&lt;code&gt;wait&lt;/code&gt; 才知道哪些进程是自己的子进程。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
