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