<?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>PIC on 大飞的博客</title>
    <link>https://www.dafei.me/tags/pic/</link>
    <description>Recent content in PIC on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 06 May 2026 05:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/pic/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（五）：中断，内核的神经系统</title>
      <link>https://www.dafei.me/posts/os-05-idt/</link>
      <pubDate>Wed, 06 May 2026 05:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-05-idt/</guid>
      <description>&lt;p&gt;键盘按下一个键，CPU 是怎么知道的？&lt;/p&gt;
&lt;p&gt;不是轮询——CPU 不会没事一直问&amp;quot;键盘有没有按键&amp;quot;。而是靠&lt;strong&gt;中断&lt;/strong&gt;（Interrupt，硬件或软件触发的信号，让 CPU 暂停当前任务去处理紧急事件）。发生了什么事，硬件主动通知 CPU，CPU 暂停手头的事，跳去处理，处理完再回来。&lt;/p&gt;
&lt;p&gt;这一章要让内核有能力响应中断。没有这个机制，内核什么都干不了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;中断的分类&#34;&gt;中断的分类&lt;/h2&gt;
&lt;p&gt;x86 的 256 个中断号分三段：&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;th&gt;例子&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;0～31&lt;/td&gt;
          &lt;td&gt;CPU 异常&lt;/td&gt;
          &lt;td&gt;除零、缺页、非法指令&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;32～47&lt;/td&gt;
          &lt;td&gt;硬件中断&lt;/td&gt;
          &lt;td&gt;时钟、键盘&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;48～255&lt;/td&gt;
          &lt;td&gt;软件中断&lt;/td&gt;
          &lt;td&gt;系统调用&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;CPU 异常是 CPU 自己触发的，比如访问了不该访问的内存，CPU 就抛一个&lt;strong&gt;缺页异常&lt;/strong&gt;（Page Fault，访问未映射的虚拟地址时 CPU 触发的异常，操作系统据此做内存按需分配）。&lt;/p&gt;
&lt;p&gt;硬件中断是外设触发的，经过 &lt;strong&gt;PIC&lt;/strong&gt;（Programmable Interrupt Controller，可编程中断控制器，负责管理外设中断请求并转发给 CPU）发给 CPU。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;idt中断的路由表&#34;&gt;IDT：中断的路由表&lt;/h2&gt;
&lt;p&gt;要处理中断，首先要告诉 CPU：每种中断发生时，去哪个函数处理？&lt;/p&gt;
&lt;p&gt;这张路由表叫 &lt;strong&gt;IDT&lt;/strong&gt;（Interrupt Descriptor Table，中断描述符表，256 项，每项对应一个中断号及其处理函数地址）。跟之前的 GDT 一样，是内存里的一张表，用 &lt;code&gt;lidt&lt;/code&gt; 指令告诉 CPU 位置。&lt;/p&gt;
&lt;p&gt;每个表项里存的是处理函数的地址，还有权限信息。CPU 一旦收到中断，就查这张表，跳到对应函数去执行。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;中断处理的细节&#34;&gt;中断处理的细节&lt;/h2&gt;
&lt;p&gt;CPU 进入中断处理函数之前，会自动把当前的执行状态压栈：&lt;code&gt;rip&lt;/code&gt;（下一条指令地址）、&lt;code&gt;cs&lt;/code&gt;、&lt;code&gt;rflags&lt;/code&gt;、&lt;code&gt;rsp&lt;/code&gt;、&lt;code&gt;ss&lt;/code&gt;。处理完之后用 &lt;code&gt;iretq&lt;/code&gt;（64 位中断返回指令）恢复状态，接着跑。&lt;/p&gt;
&lt;p&gt;有一个坑：某些异常 CPU 会&lt;strong&gt;自动压入错误码&lt;/strong&gt;（比如缺页异常），某些不会（比如除零）。为了让处理函数收到的栈结构一致，对没有错误码的异常，要手动压一个 0 占位。这件事用汇编宏做，每种异常一行，干净利落。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第一个测试故意除以零&#34;&gt;第一个测试：故意除以零&lt;/h2&gt;
&lt;p&gt;IDT 装好之后，最简单的验证方式是&lt;strong&gt;故意触发一个异常&lt;/strong&gt;，看内核能不能捕获到：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
