<?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>CoW on 大飞的博客</title>
    <link>https://www.dafei.me/tags/cow/</link>
    <description>Recent content in CoW on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Fri, 08 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/cow/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（十八）：fork 与 Copy-on-Write</title>
      <link>https://www.dafei.me/posts/os-18-fork-cow/</link>
      <pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-18-fork-cow/</guid>
      <description>&lt;p&gt;上一章每个进程有了自己的地址空间。这一章实现 &lt;code&gt;fork()&lt;/code&gt;——创建一个子进程，继承父进程的全部内存。&lt;/p&gt;
&lt;h2 id=&#34;最朴素的-fork-实现&#34;&gt;最朴素的 fork 实现&lt;/h2&gt;
&lt;p&gt;fork 的语义是&amp;quot;完整复制当前进程&amp;quot;。最直接的做法：遍历父进程页表，找到每一个物理页，分配新页，复制 4096 字节内容，给子进程建新映射。&lt;/p&gt;
&lt;p&gt;能用，但很浪费。大多数 &lt;code&gt;fork()&lt;/code&gt; 之后会紧接着 &lt;code&gt;exec()&lt;/code&gt;——旧内存根本用不上，全拷了白拷。进程堆如果有几十 MB，每次 fork 都要等好几毫秒。&lt;/p&gt;
&lt;h2 id=&#34;copy-on-write先共享写了再分&#34;&gt;Copy-on-Write：先共享，写了再分&lt;/h2&gt;
&lt;p&gt;CoW 的思路是：&lt;strong&gt;fork 时不复制，让父子共享同一批物理页；等到谁要写，再给他一份新的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;实现上分两步：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：fork 时打标记&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;遍历父进程用户页表，对每一页做两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;清掉 WRITABLE 位，变成只读&lt;/li&gt;
&lt;li&gt;打上 PAGE_COW 标志（借用 x86 页表的 bit 9，这一位 CPU 不使用，留给软件自定义）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;子进程页表复制同一个物理页地址，同样只读 + CoW。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fork 后：
  父进程 → PT → 物理页 0xA000  (只读, CoW)
                    ↑ 共享
  子进程 → PT → 物理页 0xA000  (只读, CoW)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意一个关键细节：&lt;strong&gt;PDPT/PD/PT 这三级结构页必须为子进程单独分配&lt;/strong&gt;。如果父子共用同一棵结构树，后续 &lt;code&gt;vmm_map_page&lt;/code&gt; 修改子进程时会把父进程的 PT 一起改掉，隔离失效。数据页可以共享，结构页不能。&lt;/p&gt;
&lt;p&gt;另一个细节：改完父进程页表后必须刷新 TLB。否则 CPU 缓存里还是旧的可写映射，父进程写该页不会触发 fault，CoW 形同虚设。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
