<?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>GDT on 大飞的博客</title>
    <link>https://www.dafei.me/tags/gdt/</link>
    <description>Recent content in GDT on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Fri, 22 May 2026 03:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/gdt/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（三十二）：启动 busybox sh —— 用户指针与内核页表的陷阱</title>
      <link>https://www.dafei.me/posts/os-32-busybox-init/</link>
      <pubDate>Fri, 22 May 2026 03:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-32-busybox-init/</guid>
      <description>&lt;p&gt;ch31 已经能跑 musl libc 的 hello world 了，这一章目标更高：启动 busybox sh，看到 &lt;code&gt;/ #&lt;/code&gt; 提示符。busybox 是个真正的程序，碰到的问题也更真实。&lt;/p&gt;
&lt;h2 id=&#34;准备工作&#34;&gt;准备工作&lt;/h2&gt;
&lt;h3 id=&#34;获取静态编译的-busybox&#34;&gt;获取静态编译的 busybox&lt;/h3&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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod +x busybox
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;file busybox
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# busybox: ELF 64-bit LSB executable, x86-64, statically linked&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;更新-ext2-镜像&#34;&gt;更新 ext2 镜像&lt;/h3&gt;
&lt;p&gt;busybox 需要 &lt;code&gt;/bin/sh&lt;/code&gt;、&lt;code&gt;/etc/passwd&lt;/code&gt;、&lt;code&gt;/etc/group&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-makefile&#34; data-lang=&#34;makefile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;sudo&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;mkdir&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;-p&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/tmp/ext2mnt/bin&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:#960050;background-color:#1e0010&#34;&gt;sudo&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;mkdir&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;-p&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/tmp/ext2mnt/etc&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:#960050;background-color:#1e0010&#34;&gt;sudo&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;cp&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;BUSYBOX&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/tmp/ext2mnt/bin/busybox&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:#960050;background-color:#1e0010&#34;&gt;sudo&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;cp&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;BUSYBOX&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/tmp/ext2mnt/bin/sh&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:#a6e22e&#34;&gt;printf &amp;#39;root&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;x:0:0:root:/root:/bin/sh\n&amp;#39; | sudo tee /tmp/ext2mnt/etc/passwd &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;printf &amp;#39;root&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;x:0:\n&amp;#39; | sudo tee /tmp/ext2mnt/etc/group &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;bug-1gdt-tss-描述符溢出&#34;&gt;Bug 1：GDT TSS 描述符溢出&lt;/h2&gt;
&lt;p&gt;x86_64 下 TSS 描述符是 &lt;strong&gt;16 字节&lt;/strong&gt;（两个 qword），GDT 必须为它预留两个连续槽位。之前只预留了一个，导致 TSS 描述符的高 8 字节覆盖了相邻的全局变量（恰好是 &lt;code&gt;mmap_next&lt;/code&gt;），进程一分配匿名内存就跳到奇怪的地址。&lt;/p&gt;</description>
    </item>
    <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>
