オペレーティングシステム ~ 保護とシステムコール ~ 山田浩史 hiroshiy @ cc.tuat.ac.jp 2015/05/08 復習 : OS の目的 ( 今回の話題 ) 裸のコンピュータを抽象化 (abstraction) し より使いやすく安全なコンピュータとして見せること OS はハードウェアを制御し アプリケーションの効率的な動作や容易な開発を支援する OS がないと 1 つしかプログラムが動作しない 複数のプログラムを動作させようとすると アプリ側でプログラムを切り替えるコードを記述する必要がある ( 超大変 ) 今回の話題 Word Thunder Chrome bird アプリケーションオペレーティングシステム Database CPU メモリ I/O デバイス ( ディスク等 ) 1
復習 : プロセス 実行状態にあるプログラムのこと プログラムの実行に必要なものをひっくるめて指す テキスト領域 データ領域 スタック領域 CPU のレジスタ値 プログラムカウンタ など OS はプロセス単位で管理する メモリ Hard Disk CPU プロセス execute Excel load EXCEL.EXE プログラム プロセスの何がうれしいの? CPU が複数あるように見える プロセスを切り替えながら実行していく OS の核となる部分をカーネル (kernel) と呼んだりする コンテキストスイッチをしながらプロセスを動作させる コンテキストスイッチ : 実行を別のプロセスに切り替えること 物理的に 存在する CPU 仮想 CPU レジスタ メモリ イメージ OS(kernel) Excel プロセス A レジスタ Excel プロセス B OS による抽象化 IE プロセス C 2
AGENDA OS の保護 実行モード 特権命令 システムコール スレッド (Thread) システムの保護 (protection) もしプロセスがバグって暴走 or ユーザに悪意があったら OS のデータ構造を参照 変更してしまう 保存されたレジスタに重要なデータ ( パスワード ) があるかも 他のプロセスのレジスタ値を変更されると正しく実行できない デバイスに直接アクセスできてしまう 他プロセスが保存したファイルを容易に上書きできる デバイスを変な状態のままにできちゃう OS 内の関数をむちゃくちゃ呼ぶ めったやたらにコンテキストスイッチをするシステムを 保護 (protection) する必要がある 保護がないと自由に OS にアクセスできてしまう メモリ イメージ OS プロセス A Excel プロセス B IE 3
Question どーやってシステムを保護するの? CPU にサポートしてもらう CPU の実行モード 特権モード (Kernel(Supervisor) mode) OS カーネルを実行するためのモード 特権モードでしか実行できない命令を実行できる 制御レジスタへのアクセス等 特権モードでしか参照 変更できないメモリを参照 変更できる 特権メモリ : OS がある領域等 割り込みが生じたら特権モードに切り替わる OS カーネルの割り込みハンドラが実行されるため 非特権モード (User mode) 通常のプロセスを実行するためのモード 特権命令を実行できない 特権メモリにアクセスできない 4
実行モードを使うと OS が動作するとき : 特権モード OS 開発者のコードが動く 一般人の書いたコードは動作しない プロセスが動作するとき : 非特権モード 一般人の記述したコードが動く バグったコード or ウィルスが動作する可能性 考え方 : OS はプロセスを信頼していない 特権モードで動作 非特権モードで動作 メモリ イメージ OS(kernel) Excel IE OS 自身の参照は O.K. プロセスから OS への参照は不可 OS のアクセスは O.K. HDD プロセスから直接 HDD へのアクセスは不可 特権命令 (Privileged instructions) OS 以外に実行されては困る命令 特殊なレジスタにデータを書き込む命令 例 1: 割り込みベクタの先頭番地を保持するレジスタへの書き込み 一般ユーザがこのレジスタ値を書き換え可能だと不正な割り込みハンドラを登録されてしまう x86 では LIDTR 例 2: プロセスが使用してよいメモリ領域を指定するレジスタへの書き込み 一般ユーザがこのレジスタ値を書き換え可能だとメモリの他プロセスにアクセスできてしまう 詳しくは 仮想記憶 の回で説明する I/O デバイスを操作するための命令 I/O ポートにアクセスする命令 x86 では IN, OUT 非特権モードで発行するとソフトウェア割り込みが生じる 5
I/O デバイスを使用するには 非特権モードでは I/O デバイスにアクセスできない I/O デバイスへのアクセスは OS の仕事 どう OS カーネルにお願いするか 切り替えるための命令を用意してみよう! change_to_supervisor という CPU 命令を用意する ユーザプロセスのコード /* user mode で実行中 */ /* Supervisor mode に切り替え */ change_to_supervisor /* kernel mode で実行 */ 特権命令が実行できる /* ここは kernel code ではない */ /* ここがバグってる or 悪意のあるコードだったら終わり */ システムコール (System Call) プロセスが OS に処理を依頼する窓口 ライブラリのように使える OS でなければできないことを実現してくれる I/O デバイスへの処理依頼等 例 : ファイルへのアクセスを行うもの open(),read(),write() など プロセスの生成 終了を行うもの fork(),exec(),kill() など 他にもたくさんある Linux 4.0 では 300 個以上 6
システムコール呼び出しの仕組み ソフトウェア割り込みを使って呼び出す 1. 呼び出すシステムコール番号, 引数をレジスタに入れる 2. ソフトウェア割り込みを実行する 3. 割り込みハンドラが呼び出される 4. 割り込みハンドラは, システムコール番号に従って適切なコードを実行 process user mode sysenter ( ソフトウェア割込み ) レジスタに システムコール番号 引数を入れておく ライブラリを呼び出すのとは違う printf() { call ( 関数呼び出し ) kernel mode 割込みハンドラ fork() { open() { close() { システムコール : read() の流れ read() を呼び出すと 1. OS に制御が渡り, ディスクにリクエストを発行 2. データが到着したら,OS がプロセスのメモリ領域にコピー 3. 制御をプロセスに返す 特権モード プロセス A read() 非特権モード OS システムコールを呼び出し実行モードを切り替える ディスクからのデータを取得. プロセス A に渡す ディスクにデータをリクエスト プロセス B プロセス B にコンテキストスイッチ 割り込み 割り込みハンドラへ 時間 7
システムコール v.s. ライブラリ関数 呼び出し方が違う システムコール : ソフトウェア割り込み ライブラリ関数 : call 命令 システムコールの方が呼び出しのオーバヘッドが大きい ソフトウェア割り込みは数十 百数十サイクルかかる ほとんどの命令は 1 サイクルで実行可能 CPU を高速化するための諸機能との相性が悪い パイプラインのフラッシュなど ライブラリ関数はシステムコールをうまく呼び出しているものが多い fread() など スレッド (Thread) プロセス内での並行処理を可能にする プログラムカウンタの流れを線で書いたイメージ 仮想 CPU = スレッド 従来のプロセスの考え方 ひとつのプロセスにひとつの仮想 CPU スレッドの考え方 ひとつのプロセスに複数の仮想 CPU 処理の流れを線で書くイメージ プロセス プロセス 普通のプロセスでは処理の流れはひとつ 同一のプロセス内で処理の流れをたくさん作ることもできる 8
スレッドのイメージ ひとつのプロセスが複数の仮想 CPU を持つ 仮想 CPU レジスタ メモリ イメージ OS(kernel) 物理的に 存在する CPU thread プロセス A レジスタ スタック スタック thread OS による抽象化 スタック thread プロセス B スレッドとプロセスの構成要素 スレッドの構成要素 スタック, レジスタ,PC プロセスの構成要素 最低 1 つのスレッドと, メモリ領域, プロセス制御ブロック 1 スレッドのプロセス複数スレッドのプロセス text data PCB text data PCB registers stack registers registers registers stack stack stack 9
スレッドの何がうれしいの? メモリを共有しながら並行処理が可能になる アプリケーションを作る上では非常に便利 例 1: Web ブラウザ 描画スレッド, データ受信スレッド, 入力受付スレッド,etc. スレッドではなくプロセスで作ると データを受信したら描画プロセスに情報を伝搬しないといけない 例 2: ワード 描画スレッド, 入力受付スレッド,etc. スレッドではなくプロセスで作ると 入力を受け付けたら描画プロセスにそれを通知しないといけない カーネルスレッドとユーザスレッド カーネルスレッド OS カーネル内に作られる OS によって管理される 利点 : 1 つの thread がブロックしても他 thread は動作可能 欠点 : 生成 管理が遅い ユーザスレッド ユーザプロセス内で作られる プロセスによって管理される 利点 : 生成 管理が早い 欠点 : 1 つの thread がブロックすると全体がブロック user mode user mode kernel mode OS はカーネルスレッド単位で CPU 時間を与える kernel mode 10
Linux におけるマルチスレッドプログラミング ふたつのスレッド A, B を作るプログラム例 #include <pthread.h> /* */ void *threada(void *arg) { for (;;) printf( Thread A\n ); /* A */ void *threadb(void *arg) { for (;;) printf( Thread B\n ); /* B */ int main() { pthread_t a, b; /* */ /* A, B */ pthread_create(&a, NULL, threada, NULL); pthread_create(&b, NULL, threadb, NULL); /* */ pthread_join(a, NULL); pthread_join(b, NULL); pthread_join(), pthread_create() スレッドを作る関数 pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void)) void *arg thread: 新しく作ったスレッドを表す変数へのポインタ attr: スレッドの属性を決める start_routine: スレッドが実行する関数へのポインタ arg: start_routine() に渡す引数 スレッドの終了を待つ関数 pthread_join(pthread_t thread, void **thread_return) thread で指定したスレッドの終了を待つ 指定したスレッドが返す値が thread_return に格納される 11
まとめ OS の保護 スレッドについて学んだ 実行モード 特権命令 システムコール プロセスとスレッドの違い 12