<?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/%E4%BF%9D%E6%8A%A4%E6%A8%A1%E5%BC%8F/</link>
    <description>Recent content in 保护模式 on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 06 May 2026 02:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/%E4%BF%9D%E6%8A%A4%E6%A8%A1%E5%BC%8F/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（二）：跨过这道门，CPU 才算醒了</title>
      <link>https://www.dafei.me/posts/os-02-protected-mode/</link>
      <pubDate>Wed, 06 May 2026 02:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-02-protected-mode/</guid>
      <description>&lt;p&gt;上一章我们让机器打出了第一行字。但那时候 CPU 还处于一种&amp;quot;出厂状态&amp;quot;——&lt;strong&gt;实模式&lt;/strong&gt;，最多只能用 1MB 内存，没有任何权限隔离，任何代码想写哪块内存就写哪块。&lt;/p&gt;
&lt;p&gt;这对一个操作系统来说是不可接受的。&lt;/p&gt;
&lt;p&gt;所以这一章要做一件事：把 CPU 从实模式切换到&lt;strong&gt;保护模式&lt;/strong&gt;（Protected Mode，32 位工作模式，支持内存保护和权限隔离）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;为什么叫保护模式&#34;&gt;为什么叫&amp;quot;保护&amp;quot;模式&lt;/h2&gt;
&lt;p&gt;名字来源很直接——这个模式下，内存是&lt;strong&gt;被保护的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;实模式里，程序可以随意读写任何内存地址，一个 bug 就能把整个系统搞崩。保护模式引入了两个机制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内存段的权限描述&lt;/strong&gt;：每块内存都有自己的访问权限，越界访问直接触发异常&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特权级别（Ring）&lt;/strong&gt;：内核跑在 Ring 0，用户程序跑在 Ring 3，用户程序没法直接碰内核内存&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;现代操作系统全都建立在保护模式之上。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;切换的关键gdt&#34;&gt;切换的关键：GDT&lt;/h2&gt;
&lt;p&gt;切到保护模式之前，必须先准备好 &lt;strong&gt;GDT&lt;/strong&gt;（Global Descriptor Table，全局描述符表，内存里的一张表，描述每个内存段的地址、大小和权限）。&lt;/p&gt;
&lt;p&gt;可以把 GDT 理解成一个&amp;quot;内存段登记簿&amp;quot;。CPU 进入保护模式后，所有内存访问都要查这张表，看看你有没有权限。&lt;/p&gt;
&lt;p&gt;GDT 最少需要三项：&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;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;0&lt;/td&gt;
          &lt;td&gt;空描述符（CPU 规定必须全零，不能用）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;代码段（可执行，Ring 0）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td&gt;数据段（可读写，Ring 0）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;GDT 写好之后，用 &lt;code&gt;lgdt&lt;/code&gt; 指令告诉 CPU 表在哪里，CPU 才知道去哪查。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;切换步骤&#34;&gt;切换步骤&lt;/h2&gt;
&lt;p&gt;整个切换过程就四步，缺一不可：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;关中断&lt;/strong&gt;（&lt;code&gt;cli&lt;/code&gt;）—— 切换过程不能被打断，否则 CPU 状态会乱&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加载 GDT&lt;/strong&gt;（&lt;code&gt;lgdt&lt;/code&gt;）—— 告诉 CPU 描述符表在哪&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设置 CR0 寄存器&lt;/strong&gt;（&lt;code&gt;CR0.PE=1&lt;/code&gt;）—— &lt;strong&gt;CR0&lt;/strong&gt;（控制寄存器0，控制CPU工作模式的关键寄存器）第0位置1，CPU 正式进入保护模式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;far jump 刷新流水线&lt;/strong&gt; —— 跳转的同时加载新的代码段选择子，清空 CPU 的指令预取缓存&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后这一步很容易漏掉。CPU 有指令流水线，切换模式后如果不强制刷新，可能还在用旧模式的指令译码方式，导致莫名其妙的错误。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
