第8章 システムコール,例外,割り込み
多くの CPU では,分岐命令の他にも「命令流の特別な切り替え」を伴う仕組みが提供されている.
本章では,「命令流の特別な切り替えを伴う事象」である「例外・割り込み系」のうち,「システムコール」,「例外」,「割り込み」について扱う.
8.1 用語の定義
「システムコール」,「例外」,「割り込み」の意味は,各社の CPU ごとに異なるが,本章では以下のように定義する.
- 「システムコール」:
- 特権レベルの変更命令によって明示的に発生させる命令流の切り替え
- CPU のパイプライン上の命令実行ステージで発生する
- 「例外」:
- 命令実行時に発生するまれなケースをハードウェアで検出した際,暗黙的に発生する命令流の切り替え
- CPU のパイプラインの各所で発生する
- 「割り込み」:
- 外部からの要求による命令流の切り替え
- CPU の外部で発生する
図 8.1 に各事象が CPU のハードウェア構成のどこで発生するかをまとめる.
また,本章における「例外・割込み系」の特徴を表 8.1 にまとめる.
表 8.1 の見方は,
- 発生要因:
- 命令:CPU が実行する命令がトリガー(CPU内部での原因)
- 外部:CPU の外部からの信号やイベントがトリガー
- 命令で明示:命令で意図的に起こされるか否か
- 命令と同期:
- 同期:命令の結果として必ず発生する
- 非同期:命令とは独立して発生する
- 命令の完了:発生要因の命令を完了させるか否か
- 特権レベル変更:事象によって,CPUの権限レベルが切り替わるか否か
事象 | 発生要因 | 命令で明示 | 命令と同期 | 命令の完了 | 特権レベル変更 |
---|---|---|---|---|---|
分岐命令 | 命令 | 明示 | 同期 | 完了 | 不変 |
システムコール | 命令 | 明示 | 同期 | 完了 | 変更 |
例外 | 命令 | 暗黙 | 同期 | 不可 | 変更 |
割り込み | 外部 | - | - | - | 変更 |
表 8.1 命令流の切り替えを伴う事象の分類
8.2 システムコールとその利用シーン
本章におけるシステムコールとは,前述のとおり,特権レベルの変更命令によって明示的に発生させる命令流の切り替え事象である.そのための命令を「システムコール命令」と呼ぶ.
8.2.1 特権レベル
特権レベルとは,ハードウェア資源へのアクセス権限を段階的に制限するための仕組みである.
少なくとも次の 2 つの状態に相当するものが特権レベルとして用意され,CPU はいずれかの状態に置かれる.
- ユーザーレベル:アクセス制限が最も厳しいレベル
- スーパーバイザレベル:資源への広いアクセスが許可されているレベル
ユーザーレベルから,より広い権限のスーパーバイザレベルなどへの変更を明示的に行うのがシステムコールであり,その際に命令流の切り替えが発生する.
8.2.2 システムコールの発生方法
システムコールは,各 CPU で提供されている専用のシステムコール命令によって発生する.これにより,特権レベルを変更するとともに,定められた命令流(8.5節で解説する「ハンドラ」)に切り替える.
ハンドラから元のソフトウェアに戻り,かつ特権レベルを元のソフトウェアのレベルに戻すためには,戻り専用の命令を実行する.
代表的な CPU におけるシステムコール命令及びユーザーソフトウェアなどへ戻るための命令を表 8.4 に示す.
表 8.4 を補足する(長くなるので Arm v8 AArch64 のみ記載).
- SVC (Supervisor Call):ユーザープログラム(EL0)がOSカーネル(EL1)にサービスを要求するための命令でシステムコールの実装に使われる
- HVC (Hypervisor Call):OSカーネル(EL1)がハイパーバイザー(EL2)にサービスを要求するための命令で仮想化環境で利用される
- SMC (Secure Monitor Call):ノーマルワールドからセキュアワールド(EL3)へサービスを要求するための命令でセキュリティ機構やファームウェアとの連携に使われる
- ERET (Exception Return):割り込みや例外ハンドラから元の実行状態に戻るための命令.復帰先アドレスやCPU状態は専用レジスタ(ELR, SPSR)に保存されている
命令 | Arm v8 AArch64 | RISC-V | Intel/AMD x86 |
---|---|---|---|
システムコール | svc や hvc や smc | ecall | int や sysenter や syscall |
戻り | eret | sret,mret | iret や sysexit や sysret |
表 8.4 各 CPU のシステムコール系命令
8.2.3 システムコールとその利用シーン
ソフトウェアは,特権レベルに基づいて階層的に構成することで,異常の局所化や安全性の向上や資源利用の効率化などを図れる.この階層間の遷移を実現するのがシステムコールである.一般には,ユーザーソフトウェアにおいてシステムコール命令を直接的に記述するのではなく,各種ライブラリを介してシステムコール命令を利用する.
汎用 OS(Windouws,macOS など)を用いるシステムでは,ユーザーソフトウェアから OS の機能を要求するためにシステムコールが用いられる.具体的には以下ような場合にシステムコールが用いられる.
- I/O デバイスへのアクセス要求
- メモリ領域の割り当て要求
8.3 例外とその利用シーン
本章における例外とは,前述のとおり,命令実行時に発生する稀なケースをハードウェアで検出した際,暗黙的に命令流が切り替わる事象を指す.
「稀なケース」としては,命令を正常に実行できない状況や,命令を完了させてはいけない状況がある.具体的は,以下のような場合である.
- ゼロによる除算
- 権限のない資源へのアクセス違反
こうした命令を続行できない状況をハードウェアで検出して命令流を切り替えることで,ソフトウェアによる頻繁なチェックの必要性や漏れを回避しつつ,異常や特別な状態が生じてもシステムを健全に保てるようにする.
8.3.1 例外の発生方法
例外は,命令実行時にハードウェアが特定の条件を検出することによって発生する.どのような条件により例外が発生するかは CPU によって異なる.
代表的な例外の要因を表 8.5 に示す.
表 8.5 を補足する.
- アライメント違反:メモリ上のデータ配置に関する制約を守らずにアクセスした場合に発生
- ページフォールト:プログラムがアクセスしようとした仮想メモリのページが物理メモリ上に存在しない場合に発生
- アクセス異常:プログラムが許可されていないメモリ領域にアクセスしようとしたときに発生
大まかな種別 | 例外要因 |
---|---|
命令種別関連 | 未定義命令,命令権限違反,など |
演算関連 | ゼロ除算,浮動小数点演算,など |
メモリ,仮想記憶関連 | アライメント違反,ページフォールト,アクセス異常,など |
表 8.5 主な例外の要因
8.3.2 例外とその利用シーン
例外の主な利用シーンは,命令実行時の異常な状態,もしくは特別な場合の検出である.
命令実行時の異常な状態とは,ソフトウェアにおける想定外の状態を指す.これを検出した場合の対処は,切り替え先の命令流として記述される.
命令実行の特別な場合とは,稀な事象ではあるがソフトウェアシステムとしては想定内の状態を指す.
一例としては,仮想記憶におけるページフォールトがある.例えば OS の実装においては,該当のページが主記憶上にない場合を検出するのにページフォールト例外を利用する.
これにより,そのページが必要になったらすぐに二次記憶から主記憶にコピーする「デマンドページング」を実現している.また,該当ページへの最初の書き込みを検出するのにもページフォールト例外を利用している.これにより,専用の物理メモリが必要になったらすぐに割り当てる「コピーオンライト」を実現している.
例外は,一般に OS などのシステムレベルのソフトウェアが処理する.ただし,発生元のソフトウェアに例外を通知する仕組みが設けられているシステムもある.そのような仕組みがある場合は,例外発生時の処理をユーザーソフトウェアに置いて記述することが可能になる.
8.4 割り込みとその利用シーン
本章における割り込みとは,前述のとおり,外部からの要求における命令流の切り替え事象を指す.システムコール及び例外とは異なり,CPU における命令実行に直接起因する事象ではない.
割り込みによって,命令流に対して非同期に発生するさまざまな事象を素早くかつ効率的に扱える.これによりイベント駆動のソフトウェアシステムを構築できる.
8.4.1 割り込みの発生方法
図 8.2 に示すように,割り込みを使用するためには,一般に,
- CPU 外部の割り込み要求元
- その要求を束ねる割り込みコントローラ
- そして割り込み要求を処理する CPU
のそれぞれを適切に設定する必要がある.
(1)割り込み要求元は,割り込みをする条件や,そもそも割り込み要求を行うか否かなどを設定する.多くの場合,割り込み要求元となるのは I/O デバイスである.I/O デバイスの割り込み設定は,CPU からの I/O ライトアクセスによって行う.
補足
I/O ライトアクセス:コンピュータが外部機器(ストレージや周辺機器など)に対してデータを書き込む(出力する)操作のこと
I/O デバイスの割り込み設定は,CPU からの I/O ライトアクセスによって行う:CPU が I/O デバイスの制御用レジスタ(またはデバイスファイル)にデータを書き込むことで,割り込みの許可・禁止や制御を行う
(2)割り込みコントローラは,複数の割り込み要求元からの割り込み要求を CPU に伝える優先度などを設定する.この中間階層が,CPU および割り込み要求元となることが多い I/O デバイスからしている独立していることにより,多様なシステムが構築可能となる.CPU から見ると,割り込みコントローラも I/O デバイスの一つであるので,割り込みコントローラの設定も CPU からの I/O ライトアクセスにより行う.
(3)割り込みを受け付ける側の CPU については,割り込み発生時に命令流をどのアドレスへ切り替えるかや,そもそも割り込みを受け付けるか否かなどを設定する.
割り込みの設定は煩雑なものになるが,通常,その多くの部分は OS やライブラリの層によって隠蔽されている.
8.4.2 割り込みの利用シーン
割り込みは,主に CPU 外部で発生するイベントに応じて非同期に処理を行うシステムに用いられる.
割り込みはいつ発生するかわからない事象だが,割り込みの要求元や割り込み処理用の制御回路などはシステムの共有資源であり,かつ発生した場合の優先度が高い事象である.そのため,複数の割り込み要求が多重に発生した場合の優先度や,発生頻度の調整,割り込む先のソフトウェアへの性能影響など,その適切な利用にあたってはシステム観点での設計が必要になる.
8.5 例外・割り込み系の振る舞い
システムコール,例外,割り込みは,利用シーンや用途こそ異なるが,いずれも命令流の特別な切り替えを伴う事象であることから,CPU のパイプラインにおける挙動としてはよく似ている.具体的には,図 8.3 に示すように,いずれも事象の発生時に CPU ハードウェアが自動的に PC レジスタを変更し,新しい命令流のための命令フェッチを開始する.
PC レジスタの変更によって命令流の切り替えを行うという点に関しては,分岐命令とも共通している.ただし,分岐先のアドレスを事象の発生源では明示しないこと,また,CPU の特権レベルが変更されるという点が異なる.
切り替え先の命令列は,一般に「ハンドラ」都呼ばれる.それぞれの事象に合わせて,「システムコールハンドラ」,「例外ハンドラ」,「割り込みハンドラ」と呼ぶこともある.
以降では,切り替え先の命令アドレスの選び方,CPU の状態変更,戻り先の保存,切り替え後の振る舞いについて説明する.
8.5.1 切り替え先のアドレスについて
命令流を切り替えるには,切り替え先となる命令のアドレスが必要である.このアドレスは,ユーザーソフトウェアでは指定できず,システムソフトウェアが用意した「ベクタテーブル」と呼ばれるテーブルから供給される.ベクタテーブルは,書き換え可能なテーブルとして,一般にはメモリ上に格納されている.
図 8.4 に,ベクタテーブルとハンドラ,および命令流の切り替えの関係を示す.
命令流の切り替えは以下のフローで行われる.
- まず切り替え先となる命令アドレス(ハンドラの先頭アドレス)がベクタテーブルからロード
- その値を用いて PC レジスタが変更
- 変更された PC レジスタに基づいて,新たな命令フェッチが開始
8.5.2 CPU の特権状態の変更
システムコール,例外,割り込みにおける命令流の切り替え時には,一般に CPU の特権レベルも変更される.多くの場合には,ユーザーレベルからスーパーバイザレベルへと遷移する.図 8.3 の例では,システムコール命令の実行完了を境界として,特権レベルをユーザーレベルからハイパーバイザレベルに切り替えている.
8.5.3 戻り先やコンテキストの保存
システムコール,例外,割り込みによる命令流の切り替えは,一時的な命令流の切り替えである.切り替え先の命令流(ハンドラ)における所定の処理が終わったら,一般的には元のソフトウェアの命令流へ戻る.そのために,<事象発生による命令流の切り替え時点で,(1)戻り先となる命令のアドレスや(2)コンテキスト(特権レベルやレジスタの値)を特別なレジスタに保存しておく.
(1)戻り先となるアドレスの保存は,一般には CPU ハードウェアで自動的に行う.また,戻り先として保存されるアドレスは事象に応じて異なる.
-
システムコールの場合:
- 完了後に次の命令から継続させる必要がある
- 次の命令のアドレスが戻り先として保存される
-
例外の場合:
- 例外ハンドラにおいて適切な処理ができた場合:
- 例外を発生された命令から再実行させる必要がある
- 例外を発生させた命令のアドレスが戻り先として保存されることが多い
- 例外ハンドラにおいて適切な処理ができなかった場合:
- プログラムの終了など
- 例外ハンドラにおいて適切な処理ができた場合:
-
割り込みの場合:
- 現在の命令流における@命令と命令の間」に割り込み処理なので,割り込まれた命令のアドレスが戻り先として保存される
(2)事象発生時の特権レベルも,多くの CPU では命令流の切り替え時にハードウェアによって専用のレジスタへ保存される.
(2)レジスタの値の内容については,必要に応じてハンドラ内のソフトウェアが保存するのが一般的だが,ハードウェアによっては自動的に保存される CPU もある.
8.5.4 命令流の切り替え後の振る舞い
-
システムコール命令の場合:システムコールハンドラのソフトウェアの記述内容次第で変わる
- 汎用レジスタやシステムコール命令オペランドを用いて,システムコールで要求する処理をシステムコールハンドラに伝える
-
例外の場合:発生時に CPU のパイプライン上の命令を完了させてはいけないので,該当の命令による作用をすべてキャンセルする必要がある
- アウトオブオーダー実行の CPU の場合には,命令順序を元に戻すリオーダーなどの仕組みが必要になる
-
割り込みの場合:命令流側から見るといつ発生するかわからないので,ソフトウェアにおけるアトミックな操作を中断しないようにする必要がある
- アトミックな操作の最中は一時的に割り込みを禁止にすることで実現できるが,システム全体の応答性が劣化する可能性があるので注意が必要
8.6 例外・割り込み系が CPU の動作を遅くする背景
システムコール,例外,割り込みは,通常は発生頻度が低い事象だが,一回当たりの性能ダメージは分岐命令よりも大きくなる傾向にある.疎の遅さの主な要因は,次の二つに大分できる.
- 命令流の切り替え操作に伴い,CPU のパイプラインで無駄な空きサイクルが生じる
- 切り替え先の命令流(ハンドラ)内の実行命令が遅い可能性がある
8.6.1 分岐予測が効かない
以下の理由から,システムコール,例外,割り込みによる命令流の切り替えは,一般に分岐予測の対象とはしない.従って,予測に基づく先行的な命令フェッチによって命令流の切り替えの遅さを改善することはできない.
-
システムコール:
- 命令によって必ず命令流を切り替える事象であることから,原理的には分岐予測の対象にできる
- しかし,システムコールの分岐予測はバグやセキュリティ上の課題を誘発するので,一般に CPU ハードウェアへの実装は慎重である
-
例外:
- 多くの種類の命令によって引き起こされる可能性があるため,そのすべてを分岐予測の対象にすると,分岐予測のための資源容量を圧迫してしまう
- 発生頻度が低いので,予測しても有利にはならない
-
割り込み:
- 現在の PC レジスタとは無関係に発生する事象であるので,分岐予測の基本的な機構(現在の PC レジスタの値を登録済みの命令アドレスと比較する)になじまない
8.6.2 特権レベルの変更前後は逐次的な実行が必要
(1)特権レベルの変更は,事象の発生点に先行する命令に影響してはならない一方で,事象の発生点より後続の命令では確実に反映される必要がある.そのため,事象の発生点の前後ではアウトオブオーダー実行による命令順序の入れ替えを行わない.
また,(2)事象の発生点より後続の命令に対する命令フェッチ操作において新しい特権レベルが反映されるように,命令流の切り替え境界ではパイプラインを一旦空にする必要がある(パイプラインフラッシュ).
(1),(2)をもう少し視覚的に説明してみる.
1 2 3 4 |
|
1 2 3 4 5 6 7 |
|
このように,特権レベルの変更層座の前後では一般に完全な逐次化が行われる.そのため,命令を実行できない無駄なサイクルが生じる.
8.6.3 ベクタテーブルのアクセスにおけるキャッシュミス
ベクタテーブルはメモリ上に配置される.システムコール,例外,割り込みは稀な現象であるので,キャッシュミスが起こりやすい.
8.6.4 ハンドラ内での分岐予測ミス,キャッシュミス,TLB ミス
システムコール,例外,割り込みは稀な現象であるので,切り替え先のハンドラの命令列もまれにしか実行されない.従って,ハンドラ内では分岐予測ミスやキャッシュミス,TLB ミスが発生しやすい.
8.6.5 ハンドラからの戻りも必要
ハンドラ切り替える際と同様に,にハンドラから戻るための命令も一般に分岐予測の対象としないうえ,一般に逐次化のための順序付けとパイプラインフラッシュを伴う.
8.7 ソフトウェアによる対策
命令流の特別な切り替えは,頻度が低い字事象であることから,分岐命令やキャッシュミス戸倉bると対策の優先度は低いといえる.その一方で,これが課題となる場合にはシステムレベルでの検討が必要な可能性があるという難点がある.また,各社の CPU によって大きな差異がある部分でもあり,かつ応用が多岐にわたる仕組みなので,各社の CPU に共通する対策が難しいという面もある.ここでは一般的な観点に絞ってソフトウェア的な対策を簡単に紹介する.
8.7.1 システムコール
- 明示的な命令によって発生するためソフトウェアによる制御が比較的しやすい
- 対策:発生頻度を適切に抑える
8.7.2 例外
- 命令に付随して暗黙的に発生するため対策が比較的難しい
- 発生頻度の低さなどの理由から性能ダメージは小さいと割り切ってもいい
8.7.3 割り込み
- システムの共有資源を利用し,かつ発生するタイミングがわからない優先度の高い事象のため対策が特に難しい
- 対策:割り込みの発生回数を減らす(応答性とのトレードオフ)
8.8 システムコール,例外,割り込みについての実験
8.8.1 システムコールの実行例
CPU におけるシステムコールの実行方法は単純で,OS の使用において定められたレジスタに値を設定したのち,システムコール実行用の命令を呼び出すだけである.システムコール実行用の命令は CPU によって異なる(表 8.4 を参照).
システムコールを伴うシンプルなプログラム例として,Linux における write システムコールの使用例を紹介する.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
write システムコールは,指定する出力先に,指定するアドレスからのデータを,指定するバイト数だけ出力するというものである.
x86 用の Linux では rdi,rsi,rdx などのレジスタを用いる.
実行結果は以下のようになる.
1 2 |
|
システムコールが実行されると,特権レベルが変更されるとともに,システムコールハンドラへと命令流が切り替わる.システムコールハンドラにおいてどのような処理が行われるかは OS やシステムに依存する.
通常,ユーザーのプログラムでシステムコールを直接利用することは少なく,ライブラリやプログラミング言語の処理系がシステムコールの実行を隠蔽している.たとえば,C 言語用のmライブラリ関数である printf や puts は,内部で write システムコールを実行している.
8.8.2 例外(ページフォールト)の発生例
メモリアクセスにおけるページフォールト例外を発生させるプログラムの例を示す.
このプログラムでは,mov 命令によってメモリの 0 番地からのロードを行っているだけである.
Linux では一般的に仮想アドレスの 0 番地を物理アドレスに対応付けていないため,0 番地へのアクセス時にはページフォールト例外が発生する.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
実行結果は以下のようになる.
1 2 |
|
8.8.3 発生する割り込みを確認する
プログラムの視点では,割り込みはいつどこで発生するかがわからないものである.
こうした性質から,割り込みについてはユーザーレベルでのみ如何察する短い実験用プログラムを紹介しにくいという難点がある.そこで本節では,特定の CPU や割り込みコントローラを想定した具体的なプログラム例ではなく,簡単なコマンドを用いて Linux における割り込みの要因と頻度を概観してみる.
Linux では,システムの起動時からの割り込み発生回数が /proc/interrupts ファイルに記録されている.cat コマンドを用いて /proc/interrupts の内容を表示することで,どのような要因による割り込みがどの程度発生しているかを知ることができる.
実行結果の見方を示す.
- 一番左の列(もしくは NMI などのシンボル)は割り込み要因ごとに割り当てられた識別子(IRQ 番号)
- x86 用の Linux では,0 から 31 はシステム関連,32 から 127 はデバイス関連,LOC 以降は特別な割り込みに割り当てられている
- 左から二番目から右から四番目の各列は各CPUコアでの割り込み回数を表示
- 右から三番目の列は割り込みコントローラー
- 右から二番目の列は割り込みタイプ
- 右から一番目の列はデバイス名/割り込みソース名
実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
続いて,発生する割り込みの要因ごとの頻度を確認する.何らかの操作の前後における /proc/interrupts の内容を比較することで,その操作においてどんな要因の割り込みが何回発生したかを大まかに知ることができる.
実験結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
cat コマンド間の操作内容によっては,わずか数十秒程度の間に,数十~数千回の割り込みが発生していることもある.
8.9 本章のまとめ
本節では,命令流の特別な切り替えを伴うシステムコール,例外,割り込みについて概説した.これらの命令流の特別な切り替えに伴う仕組みは,各 CPU のハードウェア使用に強く依存しており,それだけ扱いが難しいものである.しかし,これらの命令流の特別な切り替えを伴う仕組みの存在により,特権レベルに基づく保護と異常検出を活用した安全なシステム構築をはじめ,効率的な仮想記憶システムやイベント駆動システムなどの高度なソフトウェアが実現可能になる.
一方で,命令流の特別な切り替えは,CPU におけるソフトウェアの実行を遅くする要因の 1 つにもなる.ただし,これらは比較的発生する頻度が低い事象であるため,他の要因に比べると対策が必要な場面は少ない傾向にある.
もし対策が必要になる場合は,個々のソフトウェア単位ではなく,システムレベルでの挙動についての検討が必要となる可能性がある.