Closed34

CSAPP 第8章 例外的な制御フロー

bayamasabayamasa

プロセッサがイベントの発生を検出すると、例外テーブルと呼ばれるジャンプテーブルを介して、プロシージャ呼び出しが発生する。
このときに呼ばれるのはOSのサブルーチンである、例外ハンドラとなる。

例外ハンドラは3つの制御のうち一つを行う。

  1. 現在の命令位置に戻す。
  2. 次の命令位置に戻る。
  3. 元のプログラムを中断させる
bayamasabayamasa

例外には例外番号というものが割り当てられている。
例外が発生した際に、例外番号に相当するジャンプテーブルのインデクスを参照して、そのアドレスが指すプログラムをよびだす。

bayamasabayamasa

基本的に例外処理はカーネルで実装されるため、制御はカーネルに移譲し
スタックもカーネルスタックを使用したプログラムになる

bayamasabayamasa

例外は4種類に判別される

  1. 割込: interrupt
  2. トラップ: trap
  3. フォールト: fault
  4. アボート: abort
bayamasabayamasa

割込
プロセッサ外のI/Oデバイスから非同期に発生する。
ネットワークアダプタ、ディスクコントローラー、タイマーなどが該当する。

ハードウェアからチップのピンに対して、割込のシグナルを送ると同時にバスに例外番号を送信する。
これにより、割込を発生させたデバイスを識別する。

プロセッサは実行中の命令が終了した後、バスから割込命令を読み出し適切な割込ハンドラを読み出す。

bayamasabayamasa

トラップ
意図的な例外。
トラップハンドラは次の命令に制御を戻す。

トラップの例として、システムコールがある。
システムコールはユーザープログラム側でカーネルのプログラムを呼び出す橋渡し役

カーネルのプログラムはハックされるとやばいプログラムが多い。
そのためメモリ空間やシステムの呼び出しなどは、セーフティに行われる。

bayamasabayamasa

フォールト
回復の可能性があるエラー
例外が発生すると制御をフォールトハンドラに移す、もし回復できそうな場合は元の命令に制御を戻して再実行をする。
もし回復が無理だと判断した場合はabortルーチンに制御を移動して、プログラムを強制終了させる。

bayamasabayamasa

回復できる処理の典型例として、ページフォールトが存在する。
これは仮想アドレスを参照した際に、それに対応したメモリが存在しない場合に起こる。
対応したページのデータをディスクから持ってくる。

bayamasabayamasa

アボート
回復不能エラー。制御が元のプログラムに戻ってくることはない。
abort handler → abort routineの順番で呼び出される。

bayamasabayamasa

フォールトの例として

  • 0除算
  • 一般保護フォールト
    プログラムが仮想メモリ上の未定義領域を参照した場合
  • ページフォールト
    メモリページが存在しない時のフォールト

フォールトは回復可能としたが、除算エラーなどはUnix系では回復はせずにプログラムをアボートする
この辺の処理は、結局アボートしているのか処理が回復しているどっちなんでしょう

bayamasabayamasa

プロセス
複数のプログラムを同時に実行すること
これを実現するために

  • 一つのプログラムがプロセッサを専有して見えるように他のプロセスから独立した論理的制御フローを提供する
  • 一つのプログラムがメモリを専有して見えるように、各プロセスに専用のアドレス空間を提供する
bayamasabayamasa

各プロセスが独立しているように見せる箱を作る機能をOSは提供している。

bayamasabayamasa

プライベートアドレス空間
各プロセスは他のプロセスから隔離されたプライベートなアドレス空間を作る

bayamasabayamasa

プロセッサは通常モード・ビット呼ばれるビットをコントロール・レジスタにもっている。
このビットはプロセスの特権を表すビットで、モード・ビットがセットされている場合、カーネルモード(スーパーバイザーモード)で全てのメモリ、命令セットに対して実行権限を持つ。

逆にモードビットがセットされていない場合、プロセスはユーザーモードで実行できる。
このモードでは特権命令は実行できない。

bayamasabayamasa

ユーザープログラムがカーネル領域・コードにアクセスするためには、システムコールを使う必要がある。
またカーネルモードにモード変更をするのは例外よってのみ成り立つ。
→じゃあシステムコールによってカーネルにアクセスするときは、モードは切り替わっていないということなのだろうか?
トラップはそもそもシステムコールも含まれている

bayamasabayamasa

コンテキストスイッチ
カーネルはそれぞれのプロセスごとにコンテキストを管理している。
コンテキストは 汎用レジスタ、PC、ユーザースタック、カーネルスタックなどの様々なデータ構造を管理している。

マルチタスクを実行するには、現在のプロセスをプリエンプトし、既にプリエンプトされてある、プロセスをスケジューリングによって再開する。

これはカーネルのスケジューラーと呼ばれるプログラムによって実現されている。

bayamasabayamasa

fork
子プロセスを作ることができるコマンド。
子プロセスはコードセグメント、データセグメント、ヒープ、共有ライブラリ、ユーザースタックがコピーされる。
ファイルディスクリプタもコピーされる。
なのでプライベートアドレス空間がディープコピーされている感じ

bayamasabayamasa

プロセスを作成した後に回収しなかった場合、そのプロセスのために生成されたプライベートアドレス空間は残ったままになる。
そのプロセスをゾンビという。

bayamasabayamasa

ゾンビプロセスは通常initプロセスによって回収される。
initプロセスはシステムの初期化時にカーネルによって作成される。
initプロセスのpidは1、プロセスが終了することはなく、全てのプロセスの先祖に当たる位置に存在する。

bayamasabayamasa

シグナル
プロセスやカーネルが他のプロセスに対して、割込を行うことができる信号。
シグナルは番号によって管理され、それぞれ送信するステータスが異なる。

具体例

  • SIGSEGV
    セグメント違反
  • SIGINT
    キーボードからの割込
bayamasabayamasa

送信された側のプロセスは、ユーザーレベル関数のシグナルハンドラをつかってシグナルを受け取る。
シグナルは複数受け取る事ができるが、最初に送られてきたもの以外は単純に捨てられる。
→ペンディングシグナルという

bayamasabayamasa

プロセスグループ
プロセスはpidの他にプロセスグループというものを持つ。
子プロセスと親プロセスは同じプロセスグループに属する。

bayamasabayamasa

killコマンド
/bin/killを使うことで、任意のシグナルをプロセスに送信することができる。
-9: SIGKILL プログラムの強制終了

bayamasabayamasa

キーボードからCtrl + Cを入力すると、カーネルはフォアグラウンドのプロセス・グループに属する、全てのプロセスにSIGINTシグナルを送信する。

その結果全てのフォアグラウンドプロセスは終了する

bayamasabayamasa

シグナルセーフ関数
シグナルハンドラの実装はトリッキーで扱いづらい。
そのためバグを発生しやすくしてしまう。

そこで非同期シグナルセーフ関数を使用することで、バグになりにくい関数を作る。
https://codezine.jp/article/detail/4700

このスクラップは2021/10/09にクローズされました