<?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>地址空间 on 大飞的博客</title>
    <link>https://www.dafei.me/tags/%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4/</link>
    <description>Recent content in 地址空间 on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Thu, 07 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（十七）：每个进程有自己的地址空间</title>
      <link>https://www.dafei.me/posts/os-17-address-space/</link>
      <pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-17-address-space/</guid>
      <description>&lt;p&gt;前几章的进程共享同一张页表——所有进程看到的是同一片内存。这意味着进程 A 知道进程 B 的地址，就能直接读写它的数据。一个 bug 就能破坏整个系统。&lt;/p&gt;
&lt;p&gt;这一章解决这个问题：&lt;strong&gt;给每个进程一张自己的页表，互相看不见彼此的内存&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;切换页表就是切换世界&#34;&gt;切换页表就是切换世界&lt;/h2&gt;
&lt;p&gt;x86-64 的虚拟地址翻译规则写在页表里，页表的根地址放在 &lt;strong&gt;CR3 寄存器&lt;/strong&gt;里。&lt;/p&gt;
&lt;p&gt;这意味着：写 CR3 就是切换地址空间。进程 A 跑的时候 CR3 指向 A 的页表，进程 B 跑的时候 CR3 指向 B 的页表。同一个虚拟地址 &lt;code&gt;0x400000&lt;/code&gt;，在 A 里翻译到物理页 X，在 B 里翻译到物理页 Y——两边完全隔离，互不干扰。&lt;/p&gt;
&lt;p&gt;切换进程时，只需要一行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;vmm_switch&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;pml4) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __asm__ &lt;span style=&#34;color:#66d9ef&#34;&gt;volatile&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mov %0, %%cr3&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;r&amp;#34;&lt;/span&gt;((&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt;)pml4) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;硬件帮我们做了全部翻译工作。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;创建新页表&#34;&gt;创建新页表&lt;/h2&gt;
&lt;p&gt;每个进程需要自己的 PML4。但不能从空白开始——内核代码的映射必须保留，否则切过去之后 CPU 取不到内核指令，立刻 Page Fault。&lt;/p&gt;
&lt;p&gt;做法：把内核的 &lt;code&gt;kernel_pml4&lt;/code&gt; 整张复制一份作为起点，然后再往里加用户空间的映射。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;vmm_create_page_table&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;new_pml4 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;uint64_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#a6e22e&#34;&gt;pmm_alloc&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt;; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        new_pml4[i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; kernel_pml4[i];  &lt;span style=&#34;color:#75715e&#34;&gt;// 继承内核映射
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; new_pml4;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;往指定页表里建映射&#34;&gt;往指定页表里建映射&lt;/h2&gt;
&lt;p&gt;之前的 &lt;code&gt;map_page&lt;/code&gt; 只能操作当前 CR3 指向的页表。现在需要给进程建映射，但不想先切换过去，所以新接口接受一个显式的 &lt;code&gt;pml4&lt;/code&gt; 参数：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
