590 likes | 748 Views
http://www0.info.kanagawa-u.ac.jp/~kaiya/os/. オペレーティングシステム 2014. 2014/5/29 木曜 2 限 2 年前期 海谷 治彦 永松 礼夫. 目次. 第 8 章 多重プロセス 8.1 多重プロセス 8.2 プロセスの生成と消滅 8.3 プロセスの同期機能 : 排他制御 8.4 プロセスの同期機能 : 事象の連絡 8.5 プロセス間通信 8.6 デッドロック 小演習 アンケートフィードバック. 多重プロセスについて. 以下の二点が重要となる お互いに邪魔しないこと
E N D
http://www0.info.kanagawa-u.ac.jp/~kaiya/os/ オペレーティングシステム 2014 2014/5/29 木曜 2限 2年前期 海谷 治彦 永松 礼夫
目次 第8章 多重プロセス • 8.1 多重プロセス • 8.2 プロセスの生成と消滅 • 8.3 プロセスの同期機能: 排他制御 • 8.4 プロセスの同期機能: 事象の連絡 • 8.5 プロセス間通信 • 8.6 デッドロック • 小演習 • アンケートフィードバック
多重プロセスについて • 以下の二点が重要となる • お互いに邪魔しないこと • 例えば,一つの機器をとりあいするとか. • お互いに連絡がとりあえること • あるプロセスが終わったことを他に通知し,その結果を他に渡せること. • 今回の後半は,これを実現する手法の紹介が大半となる.
プロセスの生成 • 一般的にUNIXでは,すでに存在するプロセスの複製をつくり,複製の内容を作り変えることで,新しいプロセスを生成する. • この複製もとになっているプロセスを通常,「親プロセス」と呼ぶ.
プロセスの親子関係の例 UID PID PPID C STIME TTY TIME CMD root 1 0 0 Aug27 ? 00:00:05 init root 2 1 0 Aug27 ? 00:00:00 [kflushd] root 3 1 0 Aug27 ? 00:00:01 [kupdate] root 4 1 0 Aug27 ? 00:00:00 [kpiod] root 5 1 0 Aug27 ? 00:00:04 [kswapd] root 6 1 0 Aug27 ? 00:00:00 [mdrecoveryd] root 47 1 0 Aug27 ? 00:00:00 [khubd] root 547 1 0 Aug27 ? 00:00:02 /usr/sbin/sshd root 940 547 0 23:18 ? 00:00:00 /usr/sbin/sshd kaiya 941 940 0 23:18 pts/0 00:00:00 -csh kaiya 1013 941 0 23:23 pts/0 00:00:00 ps -ef kaiya 968 941 0 23:19 pts/0 00:00:00 vi a.c root 538 1 0 Aug27 ? 00:00:00 inetd root 983 538 0 23:22 ? 00:00:00 in.rlogind root 984 983 0 23:22 pts/1 00:00:00 login -- kaiya $ kaiya 985 984 0 23:22 pts/1 00:00:00 -bash kaiya 1012 985 3 23:23 pts/1 00:00:00 emacs Foo.java ある日,あるマシンのプロセスを抜粋 (ps –ef)
読み方 UID PID PPID C STIME TTY TIME CMD root 538 1 0 Aug27 ? 00:00:00 inetd root 983 538 0 23:22 ? 00:00:00 in.rlogind root 984 983 0 23:22 pts/1 00:00:00 login -- kaiya $ kaiya 985 984 0 23:22 pts/1 00:00:00 -bash kaiya 1012 985 3 23:23 pts/1 00:00:00 emacs Foo.java プロセスのもととなったコマンド名 1行が1プロセス 自プロセスの番号 親プロセスの番号 上の場合,下の図のような親子関係 になっている.子は親の複製がもとになっている. inetd in.rlogind login bash emacs …
最初のプロセス • 複製をもとにプロセスが生成されると,最初にタネになるプロセスがないとはじまらない. • Linuxには以下の2つのタネになるプロセスがある. • プロセス0 Swapper, 初期化プロセス等とよばれ,カーネル内の変数等の初期化をする. • プロセス1 Init ほとんどすべてのプロセスの先祖となる
最初のプロセスの実際 • プロセス0 Swapper • init/main.c の中の,start_kernel(void)関数が実体. • プロセス1 init • init/main.c の,init(void * unused)関数が実体. • init/main.c の中の一番最後に記述されている. • ゼロからLinuxが起動するあたりの話は時間に余裕があればやります. UID PID PPID C STIME TTY TIME CMD root 1 0 0 Aug27 ? 00:00:05 init root 2 1 0 Aug27 ? 00:00:00 [kflushd] root 3 1 0 Aug27 ? 00:00:01 [kupdate] root 4 1 0 Aug27 ? 00:00:00 [kpiod] root 5 1 0 Aug27 ? 00:00:04 [kswapd]
どうやって複製を作るか? • forkシステムコールを利用 • 実際に複製を作成する関数. • man fork 参照 • cloneシステムコールを利用 • 親と一部のデータを共有する子プロセスを作成する関数. • 上記のforkより処理が軽い. • 本講義ではとりあず扱わない.
forkのサンプルプログラム (抜粋) 1| void showchar(char c){ 2| // 省略 3| } 4| 5| main(int argc, char* argv[]){ 6| pid_t ch; 7| if((ch=fork())==0){ // child 8| showchar('c'); 9| }else if(ch>0){ // parent 10| showchar('p'); 11| } 12| 13| }
fork()関数の実行 • この実行が行われた時点でプロセスのコピーが作成される. • 実行後,自分がコピー(子供)かオリジナルかはfork()の返り値でわかる. • 返り値=0: 子供 • 返り値>0: オリジナル,値は子供のプロセスID • それ以外: fork()失敗. • 前述の例では,if文の最初の条件が成り立った分岐は子の処理の流れ,次の分岐がオリジナルの流れとなる. • 原則,分岐した流れは併合することはない.
fork1.cの説明 • 単純に自分の複製を作成するプログラム. • プログラム自体は文字cを1秒おきに20個画面に表示するダケの関数 showchar(c)を実行しているだけ. • しかし親プロセスと子プロセスで異なる文字を表示するため,プロセスが複製されたことがわかる.
fork1.c の実行と観察 • 単にコンパイルすれば動きます. • Cygwinでも動きました. • 動作させて二つの文字が表示されるのを確認する. • 同時にpsコマンド(ps –lx)で同じ名前のプロセスが存在し, • 親子関係があるのを確認する.
shellの実体 プロセス複製器 tcshの例 bashの例 コマンド名(プログラム名)をいれるとプログラムが実行されるのは,シェルといわれるプロセス複製プログラムと対話していることになる. 文献1 p.81, shellは自分で作れる!
fork2.c 簡単なshell • 文字入力をコマンドとみたてて,その実行を行うプログラム. • bashやtcshも基本的にはこの構成. • プロセス生成・消滅機構の簡単な例. • 観察事項 • 確かに他のコマンドを呼び出せるかを確認. • コマンドはフルパスで書く必要がある. • 呼び出されたコマンドともとのプログラムに親子関係があるかを ps –xl 等で確認. • 親が10秒待つようにコードをかいてある.
fork2.c の概要 1| main(int argc, char* argv[]){ 2| pid_t ch; char buf[100]; 3| 4| while(fgets(buf, 100, stdin)!=NULL){ 5| buf[strlen(buf)-1]='\0'; 6| if((ch=fork())==0){ // child 7| execl(buf, buf, NULL); // execveを呼ぶ 8| }else if(ch>0){ // parent 9| sleep(10); 10| printf("done %d\n", ch); 11| wait(0); 12| } 13| } 14| 15| }
ライブラリ関数 execl • 実行中のプロセスを他のプログラムに作り変える関数. • システムコール execveを簡易に使えるようにしたもの.(フロントエンド) • 詳細はマニュアルを参照. • execlpとexeclvとか仲間の関数が多数ある.
システムコール wait • 子プロセスの実行終了を待つための関数. • 同時に子プロセスの利用していた資源の解放も行う. • コレによって子プロセスは完全に消滅する. • コレをしないとゾンビ(後述)が残る場合がある. • 後述のモニタにおける wait とは関係ありませんので,注意してください!
プロセスの消滅とゾンビ • 計算が終わるとプロセスも消滅し,カーネル内から削除される・・・・はずである. • しかし,(死んだ)子供の情報に親がアクセスする場合をUNIXは想定しているので,計算が終わったのにプロセスのデータが残っているという状態が起こる. • この状態を,ゾンビ状態という.
ゾンビの例 詳細は zombie.c を参照.
互いに邪魔しない点からの性質 • 安全性(safety): 好ましくない状態にならない • 干渉(interfere)がおきない. • デッドロック(dead lock)が無い. • 生存性(liveness): やろうとしたことは,いつかは処理される. • 永遠に待たされることは無い • 例えば高層ビルに多数のエレベータがあるが,いつまでたっても来ないとかいうことが無い.
干渉とデットロック • 干渉(interfere) 並行処理のミスでデータ等の一貫性が失われること. • 後述の排他制御である程度回避可能. • デッドロック(dead lock) • 複数の資源を同時に必要とする複数のプロセス(スレッド)が,資源の一部をそれぞれに確保し,残りの資源が空くまで,それぞれが永久に待ってしまうこと. • 「哲学者の例題」参照.
排他制御による干渉の予防 • (プロセスだけでなく)並行処理一般に重視しなければいけない問題. • 要は共有資源を同時に処理しちゃいけない. • 一般にはロックという方法で排他制御するのが一般的.
有名な干渉の例: アホな銀行 平塚店 口座DB 池袋店 100万円 100万 10万 出金 100万 50万 入金 時間の流れ 150万 150万円 90万 90万円 ???? 100+50-10=140 じゃないの?
干渉発生のTIPS • 1つの連続した処理(コレをクリティカルセクションと呼ぶ)が終わる前に,古いデータを読んで,別処理を行っているのがマズい. • 逐次処理(not並行処理)の場合は問題なし. • 複数プロセス等による並行処理の場合,データ(変数,ファイル,レコード等)を特定のプロセスに一定期間のみ占有させる必要あり.
干渉防止に関するOSの機能 • OSはプロセスが排他的に動作するための機能を提供している. • 割り込みを禁止してクリティカルセクションが細切れにならないようにする等. • それによって,前述の干渉は防止されている. • OS授業の定番として,後述の,Lock, セマフォ,モニタと呼ばれる機構を紹介する. • これらの機構は実OSや言語に実装されている.
排他制御: 干渉回避の機構 • Lock/unlock • 資源(主にデータ等)を特定のプロセスに占有させるためのOSの機構. • あるプロセスがlock中は他のプロセスは当該資源にアクセスできない. • セマフォ (semaphre) • やはり資源を特定プロセスに占有させる(利用する)ためのOSの機構. • 単純にlock/unlockではなく,何人利用待ちかを数値としてOSが覚えておく. • 加えて,待ってるプロセスの待ち行列をOSが管理する. • 利用中のプロセスの利用が終わったら,(1)待ち人数を減らす,(2)待ってるプロセスに順番が来たことをOSが通知する.
プロセス間の同期 • あるプロセスAがプロセスBの結果に依存して処理を進める場合がある. • この場合,Aが結果を出したことを,Bがなんらかの方法で知る必要がある. • この手法として, • ポーリング • モニタ が常套手段としてある.
状況理解のための例題 プロセスB ビール業者でーす. (ギネスを補給できます) プロセスA ギネスビールが飲みたい! ギネス品切れ orz 資源 (ここでは自販機)
悪い対処 • プロセスA(酒飲みのおっさん,概ねアイリッシュ)が自販機からどかない.(図8.7a) • 他の製品を買う人を阻害. • ビール業者の補充作業も阻害. • プロセスAが数分おきに自販機の様子を見に来る.(図8.7b ポーリング) • 他の製品を買う人を邪魔. • ビール業者の補充作業にも邪魔. • 補充されたら来てくれよ,おっさん!
解決法: ギネス待ちの列を作る プロセスA型の 待ち行列 プロセスB 補給できたらAらに声かけてやるよ! ビール業者でーす. (ギネスを補給できます) ギネス品切れ 資源 (ここでは自販機)
OSにおける前述の解決案 • セマフォによる • 前述のようにセマフォはunlock待ちプロセス数と,そのプロセスの列を管理している. • 加えて待ちプロセスの通知機能もある. • よって,コレを直接に用いる. • モニタによる (後述)
セマフォ プロセスA型の 待ち行列 プロセスB 補給できたらAらに声かけてやるよ! セマフォ 3人待ち ビール業者でーす. (ギネスを補給できます) ギネス品切れ 資源 (ここでは自販機)
モニタの機構 • ある資源を共有したいプロセスが呼び出せる以下の機能をOSが提供する. • wait() これを呼び出したプロセスは待機状態となり待ち行列に入れられる. • notify() 待機中のプロセスを1つ再開させる. • notifyAll() 待機中のプロセスを全て再開させる.
生産者・消費者問題 • モニタ関係の超有名な例題 • 倉庫を介して,生産者(Producer)と消費者(Customer)が商品のやりとりをする. • 倉庫には商品をおける上限数がある. • 消費者達は倉庫に商品が無ければ,当然買えない. • 生産者達は倉庫が満杯の場合,生産を止めざるを得ない.
図による例: 両方動ける 仮に3個しか置けないとする
図による例: 生産停止 仮に3個しか置けないとする
図による例: 消費停止 仮に3個しか置けないとする
プログラムの例 所謂,生産者・消費者問題 生産過剰気味の構成にしてあるので,すぐにストック上限(本プログラムでは30に固定)あたりをうろうろする. しかし,止まりはしない.
倉庫 (Stock.java) class Stock extends IntLabel{ private static final int max=30; // 中略 // ストックから販売する synchronized void buy(){ while(empty()){ try{ wait(); } catch(Exception e){} } super.dec();// 在庫数を減 notifyAll(); } // ストックに補給する synchronized void supply(){ while(full()){ try{ wait(); } catch(Exception e){} } super.inc();// 在庫数を増 notifyAll(); } }
コード (Customer, Producer) class Customer extends LabelUpdater{ Customer(IntLabel l, Stock s){ super(l,s); } public void run(){ while(true){ stock().buy(); inc(); // 買った個数を記録 sleeping(1000); } } } class Producer extends LabelUpdater{ Producer(IntLabel l, Stock s){ super(l, s); } public void run(){ while(true){ stock().supply(); inc(); // 納品した個数を記録 sleeping(1000); } } } 双方Threadのサブ(サブ)クラス
プロセス間通信 • Inter Process Communication (IPC) • プロセス同士はデータを交換するための通信路を結ぶことができる. • 異なるマシン上のプロセス同士でも,この仕組みによって情報交換が可能となっている. • ウエブサイトとブラウザが通信できるのも,この技術のおかげ. • 初期に扱ったパイプは,この技術の一種である.
復習 パイプ&フィルタ モデル • UNIX/Linux等のコマンド言語は,複数のコマンドを接続して,より複雑な処理を「その場で」(on the fly)構成することができる. • このような処理構成法をパイプ&フィルタ モデルと呼ぶ. • 処理対象のデータが行で区分けされたテキストでないと,うまく機能しない場合が多い. • パイプは | で表現する場合が多い. • 個々のコマンドがフィルタの役目をする. • 話は簡単で, • 1個前のコマンドの出力を次のコマンドの出力とする. • だけ. • GUIでは,このような,臨機応変な対応が容易ではない.
復習’ 例 拡張子が .txt のファイルだけ列挙しました. 拡張子をとりました. 行番号をつけてみました
復習’ 概念説明 ls *.txt | sed -n s/.txt//p | cat -n ls *.txt | | sed –n s/.txt//p cat -n ファイルのリストを出力 行番号をつける 結果を 画面に出力 末尾の .txt をとる.
デッドロックについて • プロセスが同時に複数の資源を必要であるとする. • 例えば,スピーカーとタッチパネルの両方を必要とする「ピアノ遊び」のアプリ等を想像してください. • あるプロセスが一方の資源を,別のプロセスが他方の資源を確保して,他が空くのを待つとする. • プロセスAがスピーカーを確保し,プロセスBがタッチパネルを確保する. • A, Bともに残りが空くのを待つ. • OSがなんらかの介入をしなければ,プロセス群は永遠に空くのを待ってしまう.
デッドロック回避戦略 • そもそも,同時に必要な資源の一部を自由に確保させるようなことをしない. • 例えば,A, B二個の資源が必要な場合,Aが空いてなければ,とりあえずBを押さえることはしない. • OSがデッドロックになっていることを識別できれば,強制的に確保している資源を解放させる.
古典例: 哲学者の問題 • 数人の哲学者が円形テーブルを囲み食事をとろうとしている. • それぞれの哲学者の間には1本のフォークがおかれている. • 哲学者は,自分の左右におかれているフォークが2本そろわないと 食事をとることができないことになっている. • この状況で哲学者同士が言葉等で連絡しあわないとデッドロックが発生する. • 実際の人間なら言葉や目配せでけん制し合えるが,コンピュータ内のプロセスはそんなことできない!
他例題: 大工の問題 • それぞれN個のノミと木槌をN*2人の大工が共用していたとする. • とにかく空いてる道具をとって,一方が空くのを待つと,デッドロックが起きる. • 以下は N=3 の例 (それぞれ3個の道具,6人の大工)
本日の演習 • 今回紹介したサンプルプログラムの内,少なくとも一つを動作させてみよ. • 動作させた結果,理解できたこと,疑問に思ったこと等を述べよ. • どのサンプルを動かしたかも付記してね. • ちょっと改造した等の試行は歓迎します. • 加えて,前述の哲学者の問題において,どのようにデッドロックが起こるかも,わかった人は述べよ. • 別途,いつものアンケートもよろしくね.