Closed13

ハードウェアの割り込み

yukiyuki

割り込みというのは、備え付けのハードウェアから CPU への通知の方法。

カーネルが定期的に入力をチェックしに行くのではなく(=ポーリングするわけではなく)、キーボードの側からカーネルに対して通知するといったことを可能にする。

そっちの方が速いし、カーネルががんばらなくてよくなるからそうしている。

全部のデバイスを CPU につなぐというのは現実的ではないから、CPU とは分離された interrupt controller というものがすべてのデバイスの割り込みを一度集約し、それを CPU に伝えるという構成をとっている。

ハードウェアの割り込みは、CPU 例外とは違って非同期に行われる。そういうわけで、いきなり並行性をこの OS にもちこむことになるので、ちょっとバグるかもしれない。もちろん、Rust のちからを借りられる(デッドロックを除いて)。

yukiyuki

リアルワールドでのサンプルや実際のコードがあったら知りたいかも……

yukiyuki

Programmable Interrupt Controller (PIC)。

Intel 8259 には

  • Primary Interrupt Controller
  • Secondary Interrupt Controller

という2種類のインスタンスが備え付けられている。Secondary は Primary に紐付いているが、Primary は Primary でキーボードやタイマー、フロッピーディスクやシリアルポートを別個で紐付けている。

記事の図。

                     ____________                          ____________
Real Time Clock --> |            |   Timer -------------> |            |
ACPI -------------> |            |   Keyboard-----------> |            |      _____
Available --------> | Secondary  |----------------------> | Primary    |     |     |
Available --------> | Interrupt  |   Serial Port 2 -----> | Interrupt  |---> | CPU |
Mouse ------------> | Controller |   Serial Port 1 -----> | Controller |     |_____|
Co-Processor -----> |            |   Parallel Port 2/3 -> |            |
Primary ATA ------> |            |   Floppy disk -------> |            |
Secondary ATA ----> |____________|   Parallel Port 1----> |____________|

見た感じ、マウスとキーボードは切り離されているんだ。なぜそれを Primary (または Secondary) に紐づけているのか、みたいな話はちょっと知りたいかも。

それぞれのコントローラは command と data の2つの IO ポートをもっている。

yukiyuki

ChainedPics という構造体が、Primary/Secondary PIC レイアウトを表現している。unsafe なのは、PIC の設定ミスがあったときに UB するから。

pub static PICS: spin::Mutex<ChainedPics> =
    spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });

ところで、VSCode の rust-analyzer が使い始めて以来めっちゃクラッシュするのだけど、みんなそうなのかな。

yukiyuki

C-like enum を利用することで、下記のように enum を楽に u8 などの数値型と行き来させられるようになる。

#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum InterruptIndex {
    Timer = PIC_1_OFFSET,
}

impl InterruptIndex {
    fn as_u8(self) -> u8 {
        self as u8
    }

    fn as_usize(self) -> usize {
        usize::from(self.as_u8())
    }
}

普段は num-derive を使って実質的に変換してたなあ。でも、補助的な実装があまり複雑にならないのであれば、こちらを使わずに C-like enum を使ったほうがよさそう。

yukiyuki

core::ops::IndexMut を使うと、mutable でのインデックスに対するアクセスを実装できる。

a[index] というアクセスは *a.index_mut[index] のシンタックスシュガーになっている。イミュータブルな値の取り出しにしたい場合は Index トレイトを使用すること。

https://doc.rust-lang.org/core/ops/trait.IndexMut.html

struct の実装に対して enum を使ってインデックスで取り出すみたいなやり方、ドキュメント見るとできそうな感じがする。やったことない…

yukiyuki

scancode というのは、キーボードが押されたり離されたりした際にキーボードから CPU に送られる符号のこと。

https://en.wikipedia.org/wiki/Scancode

キーと scancode のマッピングには scancode sets と呼ばれる3つの標準が存在する。

  1. IBM XT
  2. IBM 3270 PC
  3. IBM AT

(それぞれ調べるとすごい古そうなコンピュータの写真付き Wikipedia が登場するw)

このあたりの実装は、下記クレートを行ってちょっと楽をする。

https://crates.io/crates/pc-keyboard

yukiyuki

最後にちょくちょく出てきたけどわかっていなかった PS/2 と呼ばれるものを調べる。

https://ja.wikipedia.org/wiki/PS/2コネクタ

画像見たらすぐにわかった。かなり昔の PC 向けのものというイメージがある。コネクタの規格のひとつ。USB とか。

このスクラップは2020/11/23にクローズされました