mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 22:37:49 +00:00
Apply suggestions from code review
Co-authored-by: Shu W. Nakamura <30687489+woodyZootopia@users.noreply.github.com>
This commit is contained in:
committed by
Philipp Oppermann
parent
00b068a6eb
commit
567ace4f8d
@@ -9,7 +9,7 @@ chapter = "Interrupts"
|
||||
# Please update this when updating the translation
|
||||
translation_based_on_commit = "81d4f49f153eb5f390681f5c13018dd2aa6be0b1"
|
||||
# GitHub usernames of the people that translated this post
|
||||
translators = ["shimomura1004"]
|
||||
translators = ["shimomura1004", "woodyZootopia"]
|
||||
+++
|
||||
|
||||
この記事では、ハードウェア割り込みを正しく CPU に転送するためにプログラム可能な割り込みコントローラの設定を行います。これらの割り込みに対処するため、例外ハンドラのときに行ったのと同じように割り込み記述子表に新しいエントリを追加しなくてはいけません。ここでは周期タイマ割り込みの受け方と、キーボードからの入力の受け方を学びます。
|
||||
@@ -27,7 +27,7 @@ translators = ["shimomura1004"]
|
||||
|
||||
## 概要
|
||||
|
||||
割り込みは、接続されたハードウェアデバイスから CPU に通知を行う方法を提供します。よって、新しい文字がないか定期的にカーネルにキーボードを確認させるかわりに([ポーリング][_polling_]と呼ばれます)、キー入力のたびにキーボードのほうからカーネルに通知することができます。この方法の場合、カーネルはなにかが起きたときだけ処置を行えばよいので、とても効率がいいです。カーネルは次のポーリングのタイミングを待たずとも即座に反応することができるので、応答時間も短くなります。
|
||||
割り込みは、接続されたハードウェアデバイスから CPU に通知を行う方法を提供します。よって、新しい文字がないか定期的にカーネルにキーボードを確認させるかわりに ([ポーリング][_polling_]と呼ばれます)、キー入力のたびにキーボードのほうからカーネルに通知することができます。この方法の場合、カーネルはなにかが起きたときだけ処置を行えばよいので、とても効率がいいです。カーネルは次のポーリングのタイミングを待たずとも即座に反応することができるので、応答時間も短くなります。
|
||||
|
||||
[_polling_]: https://ja.wikipedia.org/wiki/%E3%83%9D%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0_(%E6%83%85%E5%A0%B1)
|
||||
|
||||
@@ -48,7 +48,7 @@ translators = ["shimomura1004"]
|
||||
|
||||
## 8259 PIC
|
||||
|
||||
[Intel 8259] は 1976 年に登場したプログラム可能な割り込みコントローラ(programmable interrupt controller: PIC)です。ずいぶん前に、より新しい [APIC] によって置き換えられましたが、そのインタフェースは現在のシステムでも後方互換性のためにサポートされています。8259 PIC は APIC よりも設定がかなり簡単なので、後の記事で APIC に切り替える前に、8259 PIC を使って割り込み処理に入門することにしましょう。
|
||||
[Intel 8259] は 1976 年に登場したプログラム可能な割り込みコントローラ (programmable interrupt controller: PIC) です。ずいぶん前に、より新しい [APIC] によって置き換えられましたが、そのインタフェースは現在のシステムでも後方互換性のためにサポートされています。8259 PIC は APIC よりも設定がかなり簡単なので、後の記事で APIC に切り替える前に、8259 PIC を使って割り込み処理に入門することにしましょう。
|
||||
|
||||
[APIC]: https://ja.wikipedia.org/wiki/APIC
|
||||
|
||||
@@ -71,7 +71,7 @@ Secondary ATA ----> |____________| Parallel Port 1----> |____________|
|
||||
|
||||
この図は典型的な割り込み線の割り当てを示しています。15本の線の多くは割り当てが固定されています。例えば、セカンダリの PIC の4番目の線はマウスに割り当てられています。
|
||||
|
||||
それぞれのコントローラは、"コマンド" ポートと "データ" ポートという2つの [I/O ポート][I/O ports] を通じて設定を行うことができます。プライマリコントローラでは、これらのポートは `0x20` (コマンド)と `0x21` (データ) になります。セカンダリコントローラでは、`0xa0` (コマンド)と `0xa1` (データ)になります。PIC の設定方法の詳細は [osdev.org の記事][article on osdev.org]を見てください。
|
||||
それぞれのコントローラは、"コマンド" ポートと "データ" ポートという2つの [I/O ポート][I/O ports] を通じて設定を行うことができます。プライマリコントローラでは、これらのポートは `0x20` (コマンド) と `0x21` (データ) になります。セカンダリコントローラでは、`0xa0` (コマンド) と `0xa1` (データ) になります。PIC の設定方法の詳細は [osdev.org の記事][article on osdev.org]を見てください。
|
||||
|
||||
[I/O ports]: @/edition-2/posts/04-testing/index.md#i-o-ports
|
||||
[article on osdev.org]: https://wiki.osdev.org/8259_PIC
|
||||
@@ -112,7 +112,7 @@ pub static PICS: spin::Mutex<ChainedPics> =
|
||||
spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
|
||||
```
|
||||
|
||||
上で述べたように、PIC のオフセットを32から47の範囲に設定しています。`ChainedPics` 構造体を `Mutex` でラップすることで、次のステップで必要になる安全な可変アクセスを([`lock` メソッド][spin mutex lock]を使って)得ることができます。間違ったオフセットを指定すると未定義動作となるため、`ChainedPics::new` 関数は unsafe です。
|
||||
上で述べたように、PIC のオフセットを32から47の範囲に設定しています。`ChainedPics` 構造体を `Mutex` でラップすることで、次のステップで必要になる安全な可変アクセスを ([`lock` メソッド][spin mutex lock]を使って) 得ることができます。間違ったオフセットを指定すると未定義動作となるため、`ChainedPics::new` 関数は unsafe です。
|
||||
|
||||
[spin mutex lock]: https://docs.rs/spin/0.5.2/spin/struct.Mutex.html#method.lock
|
||||
|
||||
@@ -149,17 +149,17 @@ pub fn init() {
|
||||
}
|
||||
```
|
||||
|
||||
`x86_64` クレートの `interrupts::enable` 関数は、特別な `sti` ("set interrupts")命令を実行し外部割り込みを有効にします。ここで `cargo run` を実行するとダブルフォルトが発生します:
|
||||
`x86_64` クレートの `interrupts::enable` 関数は、特別な `sti` ("set interrupts") 命令を実行し外部割り込みを有効にします。ここで `cargo run` を実行するとダブルフォルトが発生します:
|
||||
|
||||

|
||||
|
||||
ダブルフォルトが発生する理由は、ハードウェアタイマ(正確には [Intel 8253])がデフォルトで有効になっているため、割り込みを有効にするとすぐにタイマ割り込みを受け取り始めるためです。この割り込みのためのハンドラ関数を定義していないため、ダブルフォルトのハンドラが呼ばれることになります。
|
||||
ダブルフォルトが発生する理由は、ハードウェアタイマ (正確には [Intel 8253]) がデフォルトで有効になっているため、割り込みを有効にするとすぐにタイマ割り込みを受け取り始めるためです。この割り込みのためのハンドラ関数を定義していないため、ダブルフォルトのハンドラが呼ばれることになります。
|
||||
|
||||
[Intel 8253]: https://en.wikipedia.org/wiki/Intel_8253
|
||||
|
||||
## タイマ割り込みの処理
|
||||
|
||||
[上述](#the-8259-pic)した図にある通り、タイマはプライマリの PIC の0番目の線を使います。これはタイマ割り込みは32番(0 + オフセットの32)の割り込みとして CPU に届くということです。32をハードコーディングする代わりに `InterruptIndex` enum に保存することにしましょう:
|
||||
[上述](#8259-pic)した図にある通り、タイマはプライマリの PIC の0番目の線を使います。これはタイマ割り込みは32番 (0 + オフセットの32) の割り込みとして CPU に届くということです。32をハードコーディングする代わりに `InterruptIndex` enum に保存することにしましょう:
|
||||
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
@@ -181,7 +181,7 @@ impl InterruptIndex {
|
||||
}
|
||||
```
|
||||
|
||||
Rust の enum は [C 言語ライクな enum][C-like enum] に似ていて、各ヴァリアントに直接インデックスを指定できます。 `repr(u8)` アトリビュートは、各ヴァリアントが `u8` 型で表されるよう指定しています。今後、他の例外に対してヴァリアントを追加していきます。
|
||||
Rust の enum は [C 言語ライクな enum][C-like enum] であるため、各ヴァリアントに直接インデックスを指定できます。 `repr(u8)` アトリビュートは、各ヴァリアントが `u8` 型で表されるよう指定しています。今後、他の例外に対してヴァリアントを追加していきます。
|
||||
|
||||
[C-like enum]: https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
|
||||
|
||||
@@ -211,7 +211,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(
|
||||
}
|
||||
```
|
||||
|
||||
CPU は例外に対しても外部割り込みに対しても同じように反応するため、我々が定義した `timer_interrupt_handler` は例外ハンドラと同じシグニチャを持っています(唯一の違いは、一部の例外はエラーコードをプッシュすることです)。[`InterruptDescriptorTable`] 構造体は [`IndexMut`] トレイトを実装しているので、配列の添字記法でそれぞれのエントリにアクセスすることができます。
|
||||
CPU は例外に対しても外部割り込みに対しても同じように反応するため、我々が定義した `timer_interrupt_handler` は例外ハンドラと同じシグニチャを持っています (唯一の違いは、一部の例外はエラーコードをプッシュすることです)。[`InterruptDescriptorTable`] 構造体は [`IndexMut`] トレイトを実装しているので、配列の添字記法でそれぞれのエントリにアクセスすることができます。
|
||||
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[`IndexMut`]: https://doc.rust-lang.org/core/ops/trait.IndexMut.html
|
||||
@@ -243,7 +243,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(
|
||||
|
||||
`notify_end_of_interrupt` は、プライマリとセカンダリのどちらの PIC が割り込みを送ったかを判断し、コマンドポートとデータポートを使って EOI 信号をそれぞれのコントローラに送ります。セカンダリの PIC はプライマリの PIC の入力線に接続されているため、もしセカンダリの PIC が割り込みを送った場合は、両方の PIC に信号を送る必要があります。
|
||||
|
||||
正しい割り込みベクタ番号を使うよう気をつけないと、まだ送信されていない重要な割り込みを間違って消してしまい、システムがハングしてしまうかもしれません。この関数が unsafe になっているのはこのためです。
|
||||
正しい割り込みベクタ番号を使うよう気をつけないと、まだ送信されていない重要な割り込みを間違って消してしまったり、システムがハングしてしまうかもしれません。この関数が unsafe になっているのはこのためです。
|
||||
|
||||
`cargo run` を実行すると、画面上にドットが定期的に表示されるでしょう:
|
||||
|
||||
@@ -251,14 +251,14 @@ extern "x86-interrupt" fn timer_interrupt_handler(
|
||||
|
||||
### タイマの設定
|
||||
|
||||
我々が使ったハードウェアタイマは _プログラム可能インターバルタイマ_ 、もしくは短く PIT と呼ばれています。名前が示すように、PIT は2つの割り込みの間の感覚を設定することができます。すぐに [APIC タイマ][APIC timer]に切り替えるのでここで詳細に入ることはしませんが、OSDev wiki には [PIT の設定][configuring the PIT]に関する記事が豊富にあります。
|
||||
我々が使ったハードウェアタイマは _プログラム可能インターバルタイマ_ 、もしくは短く PIT と呼ばれています。名前が示すように、PIT は2つの割り込みの間の間隔を設定することができます。すぐに [APIC タイマ][APIC timer]に切り替えるのでここで詳細に入ることはしませんが、OSDev wiki には [PIT の設定][configuring the PIT]に関する記事が豊富にあります。
|
||||
|
||||
[APIC timer]: https://wiki.osdev.org/APIC_timer
|
||||
[configuring the PIT]: https://wiki.osdev.org/Programmable_Interval_Timer
|
||||
|
||||
## デッドロック
|
||||
|
||||
これで我々のカーネルはある種の並行性を持ちました: タイマ割り込みは非同期に発生するので、どんなタイミングでも `_start` 関数に割り込むことができるのです。幸い、Rust の所有権システムは並行性に関連する多くのバグをコンパイル時に防ぐことができます。特筆すべき例外のひとつがデッドロックです。デッドロックはスレッドが決して解放されないロックを取得しようとしたときに起こり、そのスレッドは永遠にハングしてしまいます。
|
||||
これで我々のカーネルはある種の並行性を持ちました: タイマ割り込みは非同期に発生するので、どんなタイミングでも `_start` 関数に割り込み得るのです。幸い、Rust の所有権システムは並行性に関連する多くのバグをコンパイル時に防ぐことができます。特筆すべき例外のひとつがデッドロックです。デッドロックはスレッドが決して解放されないロックを取得しようとしたときに起こり、そのスレッドは永遠にハングしてしまいます。
|
||||
|
||||
我々のカーネルでは、既にデッドロックが起きる可能性があります。我々が実装した `prinln` マクロは `vga_buffer::_print` 関数を呼び出しており、_print 関数はスピンロックを使って[グローバルな `WRITER` をロックする][vga spinlock]ということを思い出してください:
|
||||
|
||||
@@ -284,8 +284,8 @@ _print 関数は `WRITER` をロックし、`write_fmt` を呼び出し、そし
|
||||
1 | `print` が `WRITER` をロック |
|
||||
2 | | **割り込みが発生**、割り込みハンドラが動き出す
|
||||
3 | | `println!` を呼び出す |
|
||||
4 | | `print` が `WRITER` をロックしようとする(既にロック済み)
|
||||
5 | | `print` が `WRITER` をロックしようとする(既にロック済み)
|
||||
4 | | `print` が `WRITER` をロックしようとする (既にロック済み)
|
||||
5 | | `print` が `WRITER` をロックしようとする (既にロック済み)
|
||||
… | | …
|
||||
_(決して起こらない)_ | _`WRITER` のロックを解放する_ |
|
||||
|
||||
@@ -312,7 +312,7 @@ QEMU で実行すると以下のような出力が得られます:
|
||||
|
||||

|
||||
|
||||
限られた数のハイフンが表示され、ついに最初のタイマ割り込みが発生したことがわかります。そしてタイマ割り込みハンドラがドットを表示しようとするとデッドロックするので、システムがハングしてしまいます。これが上記の出力でドットが表示されていない理由です。
|
||||
限られた数のハイフンが表示されたのち、最初のタイマ割り込みが発生したことがわかります。そしてタイマ割り込みハンドラがドットを表示しようとするとデッドロックするので、システムがハングしてしまいます。これが上記の出力でドットが表示されていない理由です。
|
||||
|
||||
タイマ割り込みは非同期に発生するので、実際のハイフンの数は実行するたびに変わります。この非決定性が、並行性に関するバグのデバッグを非常に難しくします。
|
||||
|
||||
@@ -336,7 +336,7 @@ pub fn _print(args: fmt::Arguments) {
|
||||
}
|
||||
```
|
||||
|
||||
[`without_interrupts`] 関数は[クロージャ][closure]を引数に取り、割り込みが発生しない状態で実行します。これを使えば `Mutex` がロックされている間は割り込みが発生しないことを保証できます。このカーネルを実行すると、今度はハングせずに実行が続きます(ドットがないように見えますが、スクロールが速すぎるためです。例えば `for _ in 0..10000 {}` をループ内で実行するなどで表示速度を遅くしてみてください。)
|
||||
[`without_interrupts`] 関数は[クロージャ][closure]を引数に取り、これを割り込みが発生しない状態で実行します。これを使えば `Mutex` がロックされている間は割り込みが発生しないことを保証できます。このように修正したカーネルを実行すると、今度はハングせずに実行が続きます。(ドットがないように見えますが、これはスクロールが速すぎるためです。例えば `for _ in 0..10000 {}` をループ内で実行するなどで表示速度を遅くしてみてください。)
|
||||
|
||||
[`without_interrupts`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/interrupts/fn.without_interrupts.html
|
||||
[closure]: https://doc.rust-lang.org/book/ch13-01-closures.html
|
||||
@@ -360,7 +360,7 @@ pub fn _print(args: ::core::fmt::Arguments) {
|
||||
}
|
||||
```
|
||||
|
||||
割り込みを無効化することは一般的な解決策ではないことは覚えておいてください。レイテンシ、つまりシステムが割り込みに反応するまでの時間の最悪値を増加させるという問題があります。そのため割り込みの無効化はごく短時間に限るべきです。
|
||||
割り込みを無効化することを一般的な解決策としてはならないことは覚えておいてください。割り込みの無効化は、レイテンシ、つまりシステムが割り込みに反応するまでの時間の最悪値を増加させるという問題があります。そのため割り込みの無効化はごく短時間に限るべきです。
|
||||
|
||||
## 競合状態を修正する
|
||||
|
||||
@@ -396,7 +396,7 @@ fn test_println_output() {
|
||||
}
|
||||
```
|
||||
|
||||
このテストでは、VGA バッファに文字列を出力したあと `buffer_chars` 配列を手動でひとつずつチェックしています。`println` 関数を実行したあと、表示された文字の読み取り処理を行うまでの間にタイマ割り込みハンドラが動作するかもしれず、このとき競合状態になります。ただ、これは Rust がコンパイル時に完全に防ぐことができる危険な _データ競合_ ではないことに注意してください。詳細は [_Rustonomicon_][nomicon-races] を参照してください。
|
||||
このテストでは、VGA バッファに文字列を出力したあと `buffer_chars` 配列を手動でひとつずつチェックしています。`println` 関数を実行したあと、表示された文字の読み取り処理を行うまでの間にタイマ割り込みハンドラが動作するかもしれず、このとき競合状態になります。ただ、これは危険な _データ競合_ ではないことに注意してください―― Rust はデータ競合をコンパイル時に完全に防ぐことができます。詳細は [_Rustonomicon_][nomicon-races] を参照してください。
|
||||
|
||||
[nomicon-races]: https://doc.rust-lang.org/nomicon/races.html
|
||||
|
||||
@@ -426,17 +426,17 @@ fn test_println_output() {
|
||||
|
||||
- `lock()` メソッドを明示的に使い、テスト実行中はずっと writer をロックし続けるようにします。`println` の代わりに、既にロックされた writer に表示を行うことができる [`writeln`] マクロを使います。
|
||||
- 他のデッドロックを防ぐため、テスト実行中は割り込みを無効化します。そうでないと writer がロックされている間に割り込みが入ってきてしまうかもしれません。
|
||||
- テスト実行前はタイマ割り込みハンドラが実行される可能性があるので、文字列 `s` を出力する前に追加の改行文字 `\n` を出力するようにします。これにより、タイマハンドラが現在の行に既に出力した `.` 文字によってテストが失敗するのを避けています。
|
||||
- テスト実行前にタイマ割り込みハンドラが実行される可能性は依然としてあるので、文字列 `s` を出力する前に追加で改行文字 `\n` を出力するようにします。これにより、タイマハンドラが現在の行に既に出力した `.` 文字によってテストが失敗するのを避けています。
|
||||
|
||||
[`writeln`]: https://doc.rust-lang.org/core/macro.writeln.html
|
||||
|
||||
上記の変更によって、`cargo test` は再び必ず成功するようになります。
|
||||
|
||||
これはテストが失敗するだけの無害な競合状態でした。想像できると思いますが、他の競合状態はその非決定的な性質のためずっとデバッグが大変になり得ます。幸運なことに Rust は、システムのクラッシュやメモリ破壊を含むあらゆる種類の未定義動作を引き起こす最も深刻なタイプの競合状態であるデータ競合から我々を守ってくれます。
|
||||
これはテストが失敗するだけの無害な競合状態でした。想像できると思いますが、他の競合状態はその非決定的な性質のためずっとデバッグが大変になり得ます。幸運なことに Rust は、システムのクラッシュや無兆候でのメモリ破壊を含むあらゆる種類の未定義動作を引き起こす最も深刻なタイプの競合状態であるデータ競合から我々を守ってくれます。
|
||||
|
||||
## `hlt` 命令
|
||||
|
||||
これまで我々は、`_start` や `panic` 関数の末尾で単純なループ文を使ってきました。これはずっと CPU を回し続けるので、期待通りに動作します。しかしこれはなにも仕事がない場合でも CPU が全速力で動作し続けることになるので、とても非効率です。カーネルを動かしているときにタスクマネージャを見れば問題がすぐに確認できるでしょう: QEMU のプロセスは、常時 CPU 時間のほぼ 100% を必要とします。
|
||||
これまで我々は、`_start` や `panic` 関数の末尾で単純なループ文を使ってきました。これはずっと CPU を回し続けるので、期待通りに動作します。しかしこれはなにも仕事がない場合でも CPU が全速力で動作し続けることになるので、とても非効率です。カーネルを動かしているときにタスクマネージャを見ればこの問題がすぐに確認できるでしょう: QEMU のプロセスは、常時 CPU 時間のほぼ 100% を必要とします。
|
||||
|
||||
我々が本当にやりたいことは、次の割り込みが入るまで CPU を停止することです。これにより CPU はほとんど電力を使わないスリープ状態に入ることができます。[hlt 命令][`hlt` instruction]はまさにこれを行うものです。この命令を使ってエネルギー効率のいい無限ループを作ってみましょう:
|
||||
|
||||
@@ -452,7 +452,7 @@ pub fn hlt_loop() -> ! {
|
||||
}
|
||||
```
|
||||
|
||||
`instructions::hlt` 関数はアセンブリ命令の[薄いラッパ][thin wrapper]です。この命令はメモリ安全性を損なわないので安全です。
|
||||
`instructions::hlt` 関数はアセンブリ命令の[薄いラッパ][thin wrapper]です。この命令はメモリ安全性を損なわないので unsafe ではありません。
|
||||
|
||||
[thin wrapper]: https://github.com/rust-osdev/x86_64/blob/5e8e218381c5205f5777cb50da3ecac5d7e3b1ab/src/instructions/mod.rs#L16-L22
|
||||
|
||||
@@ -554,7 +554,8 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(
|
||||
}
|
||||
```
|
||||
|
||||
[上述](#the-8259-pic)した図で見たように、キーボードはプライマリ PIC の1番目の線を使います。これはキーボード割り込みは33番(1 + オフセットの32)の割り込みとして CPU に届くということです。このインデックスを新たな `Keyboard` のヴァリアントとして `InterruptIndex` enum に追加します。enum ヴァリアントの値はデフォルトでは前の値に1を足したもの、すなわち33になるので、値を明示的に指定する必要はありません。割り込みハンドラでは、`k` の文字を表示して割り込みコントローラに EOI 信号を送ります。
|
||||
[上述](#8259-pic)した図で見たように、キーボードはプライマリ PIC の1番目の線を使います。これはキーボード割り込みは33番(1 + オフセットの32)の割り込みとして CPU に届くということです。このインデックスを `Keyboard` というヴァリアントとして新たに `InterruptIndex` enum に追加します。enum ヴァリアントの値はデフォルトでは前の値に1を足したもの、すなわち33になるので、値を明示的に指定する必要はありません。割り込みハンドラでは、`k` の文字を表示して割り込みコントローラに EOI 信号を送ります。
|
||||
[上述](#8259-pic)した図で見たように、キーボードはプライマリ PIC の1番目の線を使います。これはキーボード割り込みは33番 (1 + オフセットの32) の割り込みとして CPU に届くということです。このインデックスを新たな `Keyboard` のヴァリアントとして `InterruptIndex` enum に追加します。enum ヴァリアントの値はデフォルトでは前の値に1を足したもの、すなわち33になるので、値を明示的に指定する必要はありません。割り込みハンドラでは、`k` の文字を表示して割り込みコントローラに EOI 信号を送ります。
|
||||
|
||||
これでキーを押したときに画面上に `k` の文字が表示されます。しかしこれは最初のキー入力に対してしか動作しません。キーを押し続けたとしても、それ以上 `k` の文字が画面上に表示されることはありません。この理由は、我々が押されたキーの _スキャンコード_ と呼ばれる値を読み取らない限りは、キーボードコントローラは別の割り込みを送らないためです。
|
||||
|
||||
@@ -600,7 +601,7 @@ _スキャンコードセット_ と呼ばれるスキャンコードとキー
|
||||
[IBM 3270 PC]: https://en.wikipedia.org/wiki/IBM_3270_PC
|
||||
[IBM AT]: https://ja.wikipedia.org/wiki/PC/AT
|
||||
|
||||
デフォルトでは、PS/2 キーボードはスキャンコードセット1 ("XT") をエミュレートします。このセットでは、スキャンコードの下位の7ビットでキーを表し、最上位の1ビットで押したか("0")離したか("1")を表します。例えばエンターキーのような元の IBM XT のキーボードに存在しないキーに対しては、エスケープである `0xe0` とそのキーを表すバイトという連続した2つのスキャンコードを生成します。スキャンコードセット1の全てのスキャンコードと対応するキーについては [OSDev Wiki][scancode set 1] を確認してください。
|
||||
デフォルトでは、PS/2 キーボードはスキャンコードセット1 ("XT") をエミュレートします。このセットでは、スキャンコードの下位の7ビットでキーを表し、最上位の1ビットで押したか ("0") 離したか ("1") を表します。例えばエンターキーのような元の IBM XT のキーボードに存在しないキーに対しては、エスケープである `0xe0` とそのキーを表すバイトという連続した2つのスキャンコードを生成します。スキャンコードセット1の全てのスキャンコードと対応するキーについては [OSDev Wiki][scancode set 1] を確認してください。
|
||||
|
||||
[scancode set 1]: https://wiki.osdev.org/Keyboard#Scan_Code_Set_1
|
||||
|
||||
@@ -706,7 +707,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(
|
||||
|
||||
[`HandleControl`]: https://docs.rs/pc-keyboard/0.5.0/pc_keyboard/enum.HandleControl.html
|
||||
|
||||
各割り込みでは、ミューテックスをロックし、キーボードコントローラからスキャンコードを読み取り、それを読み取ったスキャンコードを `Option<KeyEvent>` に変換する [`add_byte`] メソッドに渡します。KeyEvent は、そのイベントを起こしたキーと、それが押されたのか離されたのかの情報を含んでいます。
|
||||
各割り込みでは、ミューテックスをロックし、キーボードコントローラからスキャンコードを読み取り、それを読み取ったスキャンコードを `Option<KeyEvent>` に変換する [`add_byte`] メソッドに渡します。[`KeyEvent`] は、そのイベントを起こしたキーと、それが押されたのか離されたのかの情報を含んでいます。
|
||||
|
||||
[`Keyboard`]: https://docs.rs/pc-keyboard/0.5.0/pc_keyboard/struct.Keyboard.html
|
||||
[`add_byte`]: https://docs.rs/pc-keyboard/0.5.0/pc_keyboard/struct.Keyboard.html#method.add_byte
|
||||
@@ -728,7 +729,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(
|
||||
|
||||
## まとめ
|
||||
|
||||
この記事では、外部割り込みを有効にする方法とそれを処理する方法について説明しました。8259 PIC 自身とそのプライマリ/セカンダリレイアウト、割り込み番号をマッピングし直す方法、そして "end of interrupt" 信号について学びました。我々はハードウェアタイマとキーボード向けの割り込みハンドラを実装し、次の割り込みまで CPU を停止させる `hlt` 命令について学びました。
|
||||
この記事では、外部割り込みを有効にする方法とそれを処理する方法について説明しました。8259 PIC とそのプライマリ/セカンダリレイアウト、割り込み番号をマッピングし直す方法、そして "end of interrupt" 信号について学びました。我々はハードウェアタイマとキーボード向けの割り込みハンドラを実装し、次の割り込みまで CPU を停止させる `hlt` 命令について学びました。
|
||||
|
||||
これで我々はカーネルと対話することができるようになり、小さなシェルやシンプルなゲームを作るための基本的な構成要素を得ることができました。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user