39
INTERRUPTS ON XV6 @syuu1228

Interrupts on xv6

Embed Size (px)

DESCRIPTION

カーネル/VM勉強会 第二回 OS基礎(xv6)

Citation preview

Page 1: Interrupts on xv6

INTERRUPTS ON XV6

@syuu1228

Page 3: Interrupts on xv6

おさらい このあたりは「はじめて読む 486」を読んでれば分かってるはず

わかってない人がこの場にいるとも思えないんだけれども…

Page 4: Interrupts on xv6

割り込みとは 現在CPU上で実行しているプログラムを停止して別の処理を実行する為にCPUが持つ機能UNIX上のアプリケーションにおける「シグナル」に近い概念

二種類あるハードウェア割り込み

○ ハードウェアからCPUへ「キーボード押されたよ」などのメッセージを通知するのに使う

○ 単に「割り込み」と呼ばれる事もあるソフトウェア割り込み

○ ソフトウェアから割り込みを起こせる○ モード切替に利用される→システムコールに使う

Page 5: Interrupts on xv6

ハードウェア割り込みの例 ATAコントローラ

HDDへの read/writeリクエストの完了通知 シリアルポート・ NIC

受信通知送信完了通知

タイマー一定期間毎に割り込み指定期間後に割り込み

Page 6: Interrupts on xv6

ATA受信割り込みの例1. ユーザプログラムがファイルに対してwriteシステムコールを実行

2. ファイルシステムがバッファキャッシュへ書き込み(ダーティフラグON)

3. バッファキャッシュがHDDへライトバック

4. ATAドライバが ATAコントローラへ書き込み要求を送信

5. 書き込みが終了したらバッファキャッシュのダーティフラグをクリア

Page 7: Interrupts on xv6

書き込みが終了したら?割り込みを使わない場合 ATAコントローラのステータスレジスタを確認し続ければ完了したかどうか分かる

send_write_request(buffer);while(read_ata_status() &

ATA_STATUS_BUSY);

buffer.is_dirty = 0;

busy waitになってしまう→ CPUの無駄

Page 8: Interrupts on xv6

書き込みが終了したら?割り込みを使う場合 送信完了時の処理は、割り込み着信時に実行すればいい書き込み処理:

send_write_request(buffer);sleep(buffer);

割り込みハンドラ:buffer = find_buffer(ata_reg);buffer.is_dirty = 0;wakeup(buffer);

busy waitしていない分の CPU時間を別の処理(別のプロセスを実行など)にまわせる

sleep()/wakeup()で待ち合わせ

Page 9: Interrupts on xv6

割り込みハンドラ 割り込みを受けた時に CPUが実行するプログラム(関数呼び出しを伴わない関数のようなもの)

割り込みハンドラ実行時に CPUがレジスタの状態をスタックへ退避

ハードウェア割り込みの場合は、デバイスのレジスタを読んで割り込みの処理に応じた最小限の処理を実行

割り込みハンドラから終了用の命令を実行して、スタックから割り込み前の状態へ復帰

Page 10: Interrupts on xv6

プリエンプティブマルチタスクと割り込み プリエンプティブマルチタスク:カーネルがプロセスへの CPUの割り当て時間を管理、タイマーを使ってアプリケーションを一定時間毎に切り替え

プロセスが無限ループなどに陥りカーネルへ制御を渡さない場合でもタイマー割り込みにより中断され、割り当て時間を使い切ったら他のプロセスへ切り替えられる

割り込みがないと実現出来ない

Page 11: Interrupts on xv6

例外 割り込みとは別の概念だが、実装上の共通点は多いため x86では共通の仕組みで取り扱われているアプリケーション上の例外とほぼ意味は同じだが、 CPU上の機能

例外の例:ゼロ除算、無効な命令、ページフォールト、一般保護例外、ブレークポイント

Page 12: Interrupts on xv6

割り込み/例外ベクタ 0-255のベクタ番号が割り込みと例外に割り当てられる例外は要因毎に 0-19の固定された値ソフト割り込みは 0-255へ割り込み可ハードウェア割り込みは LAPIC経由の場合

16-255へ割り込み可 OSは IDT( Interrupt Descriptor

Table)を作成し、 CPUへアドレスを設定

Page 13: Interrupts on xv6

IDT( Interrupt Descriptor Table)と IDTR

各ベクタの割り込みハンドラの情報を持つ Gate Descriptorが並んでいるメモリ上のテーブル

テーブルの先頭アドレスと Limit値(テーブルのサイズ、 255未満の Gate数に対応)を CPUの IDTRに書き込む事により設定( IDTRを設定しない限り、CPUは割り込み時に何をしたらいいか分からない)

Page 14: Interrupts on xv6

Gate Descriptor 前述の「割込みハンドラ」のアドレスを持つ構造体

単なる関数へのポインタではなくて、セグメントの設定とか権限(Ring)とか 16 bit/32 bitのモード選択とか、幾つかのパラメータを含む

Task gate, Interrupt gate, Trap gateの三種類あるTask gateはHWのマルチタスク機能でハンドリングする方法で今は使われていない

Interrupt gateと Trap gateは普通に割り込みハンドラへジャンプする方法EFLAGS.IFフラグをクリアするか否かが違い

Page 15: Interrupts on xv6

IDT,IDTR,Gate descriptorの関係

Page 16: Interrupts on xv6

xv6の IDT周りを見てみる main.c:main()

tvinit(); 割り込みベクタの設定mpmain();

○ idtinit(); IDTRの設定 trap.c:tvinit()

for(0..255) SETGATE(idt[i] … vectors[i] …)idt[i]に vectors[i]を割り込みハンドラとした gate descriptorを設定

システムコールだけDPL_USERに trap.c:idtinit()

x86.h:lidt()○ asm volatile(“lidt (%0)” :: “r” (pd)); LIDT命令で idtのアドレスを設定

Page 17: Interrupts on xv6

xv6の割り込み・例外ハンドラ vectors.pl→vectors.S

vectorsは vector0..vector255を指すポインタが入っているテーブルで、 vectorNは alltrapsを呼んでいる

trapasm.S全レジスタ push pushl %esp call trap全レジスタ pop iret (割り込みからの復帰)

trap.c:trap() trapasm.Sから引数として渡されたスタックポインタを構造体として参照

trapnoがシステムコールの場合・タイマ/ IDE/キーボード/シリアル割り込みの場合についてそれぞれのハンドル関数をコールしている

Page 18: Interrupts on xv6

CPU内部の割り込みの話おわり 実際には、割り込みは CPUの外からやってくる

これを受け取ってどう CPUへ割り込みをかけるかを司る、仲介役を果たす「割り込みコントローラ」が必要

割り込みコントローラのずっと向こう側に、実際に割り込みを送っているデバイスが居る

さぁ、 CPUの向こう側の話をしよう

Page 19: Interrupts on xv6

Advanced Programmable Interrupt Controller (APIC)

オリジナルの x86アーキテクチャでは割り込みコントローラとして PIC(8259)を使っていたが、 P6以降の x86アーキテクチャでは APICへ移行( PICも未だ存在していて使える)

CPU毎に存在し CPUに内蔵されているLocal APICと、 ICH( Southbridge)に内蔵されている I/O APICから構成されている

Page 20: Interrupts on xv6

Local APICと I/O APIC

Local APICローカル割り込みのベクタ番号設定、割り込みベクタ番号通知、 EOIなど一般的な割り込みコントローラの役割

I/O APICどの外部割り込みをどの Local APICに振るか等を決める役割

Page 21: Interrupts on xv6

Local APICと I/O APIC

Page 22: Interrupts on xv6

Local APIC内のコンポーネントなにやら割り込みコントローラ以外にも幾つか機能が乗っている タイマーカーネルの tickに使うことが多い

温度センサ パフォーマンスモニタリングカウンタ

Page 23: Interrupts on xv6

APIC ID

Local APIC ID Registerから取得可 APIC IDでプロセッサを一意に特定 この IDで I/O APICから割り込み先

CPUを指定

Page 24: Interrupts on xv6

Local Vector Table

ローカル割り込みのベクタ番号を設定CMCI(Corrected Machine Check Interrupt)TimerThermal MonitorPerformance CounterLINT0/1 レガシーデバイスとかError

外部割り込みはここで設定しない

Page 25: Interrupts on xv6

割り込みの受信 IRR: Interrupt Request Register

CPUが未処理の割り込みベクタ番号にビットが立っている

ISR: In Service Register次に割り込む候補EOIへ書き込まれた時に IRR→ISRへビットが更新される

EOI: End Of Interrupt Register割り込みハンドラ終了通知として 0を書き込む(例外有り)

Page 26: Interrupts on xv6

I/O APIC (ICH10の場合 )

24本の割り込みをサポート Redirection Tableで割り込み先 Local

APICを決定( 24エントリのテーブル)

Page 27: Interrupts on xv6

Redirection Table

各 IRQの宛先 APIC ID, Vectorなどを設定Destination 宛先 APIC IDMask 割り込みマスクTrigger Mode Edge/LevelRemote IRRInterrupt Input Pin PolarityDelivery Status Idle/PendingDestination Mode Physical/LogicalDelivery Mode Fixed/Lowest…Vector Vector no of interrupt

Page 28: Interrupts on xv6

Destination Mode

Physical Destination ModeLAPIC上の Local APIC ID Registerの値を指定する事により CPUを一意に特定

Logical Destination ModeLAPIC上の Logical Destination Registerと

Destination Format Registerの値と Addressされた IDがマッチするかどうかで判別

Page 29: Interrupts on xv6

Logical Destination Mode Destination Format Registerでモード選択 flat model

各 LAPIC毎に LDRへ異なる bitを立て、宛先アドレスには複数の bitを立てる事で複数のCPUのを選択可能

アドレスが 8bitしか無いので対応 CPU数は 8まで cluster model

LDRと宛先アドレスを 4bitで分割、双方 cluster IDと logical IDを持つ

使い方がよくわからない… 8より多いCPUをサポートする為の階層化?

Page 30: Interrupts on xv6

Delivery Mode

FixedDestinationに指定された全ての CPUへ割り込み

Lowest priorityDestinationのうち LAPICの Task Priority Registerの値が最も小さい CPUへ割り込み→ TPRを制御する事で動的に割り込み先を変更可能

Page 31: Interrupts on xv6

xv6の LAPIC周りを見てみる main.c:main()

lapicinit(); LAPICを初期化 lapic.c:lapicinit()

LAPIC有効化タイマー初期化LINT0/LINT1、パフォーマンスカウンタ割り込み無効化

エラー割り込みのベクタ番号設定エラーステータスレジスタクリア割り込みクリア

Page 32: Interrupts on xv6

割り込みハンドラ周りのLAPIC trap.c:trap()

lapiceoi(); EOIレジスタへ 0書き込み lapic.c:lapiceoi()

Page 33: Interrupts on xv6

xv6の I/O APIC周り main.c:main()

ioapicinit();ideinit();

ioapic.c:ioapicinit()全ての割り込みを無効に

ide.c:ideinit()ioapicenable(IRQ_IDE, ncpu – 1);

IRQ_IDEを APIC ID=ncpu -1へ送るよう設定質問: Delivery Modeと Destination Modeは何?

Page 34: Interrupts on xv6

xv6の I/O APIC周り console.c:consoleinit()

ioapicenable(IRQ_KBD, 0) uart.c:uartinit()

ioapicenable(IRQ_COM1, 0)

Page 35: Interrupts on xv6

PIC(8259) IOAPICと同様 ICHに存在

IOAPIC(又は cpu0の Local APIC)へ接続されている

「古い割り込みコントローラ」のように紹介したが、レガシデバイスが接続されているのでそのようなデバイスを使う場合は PICを使う必要がある

xv6ではレガシデバイスを使う為、PIC→IOAPICの順で割り込みの設定を行なっているideinit(),consoleinit(),uartinit()で呼んでいる

ioapicenable()の手前にある picenable()

Page 36: Interrupts on xv6

レガシデバイス 以下の様なデバイス

PS/2 キーボード/マウスIDECOM0, COM1Floppyetc…

Page 37: Interrupts on xv6

MSI: Message Signal InterruptMSI-X: MSI Extended Interrupt

物理的な割り込み線を用いず、(多分、PCIの?)メッセージング機構により割り込みを通知する仕組み

PCI Expressからサポート必須 IO APICを経由しない(!) →割り込み先どうやって決めんの?

PCI Configuration Spaceに設定

Page 38: Interrupts on xv6

MSIの割り込み設定 詳細はここをみてね IOAPICと同じく、 Logical/Physicalの

Destination Mode、 Fixed/Lowestなどの Delivery modeが存在

Page 39: Interrupts on xv6

x2apic

拡張された APIC 何が拡張されたか?

色々あるはずだが、少なくとも Local APIC IDや Destinationのフィールド長が拡張されている

より沢山の CPUをサポート出来るようになった