-title: プロセスの概念とその基本操作
--目的
この実験では、Unixの普通のプロセスと軽量プロセス(thread)を用いて、
プロセスの概念と基本的な操作である生成と終了について学ぶ。
シェル・レベルのコマンドを用いてUNIXのプロセスを観察する。
C言語により、プロセスの生成・終了を行うプログラムを作成する。
---関連科目
情報204 オペレーティングシステム 必修、2単位
教科書: Operating System Concept
---準備
授業のWWWを
読んで、プロセスの概念をつかみなさい。
プロセスとプログラムの違いは、どこにあるのか。プロセスに関する次の
キーワードの意味を調べなさない。
- プロセスの状態と状態遷移
- FCFS(FIFO)スケジューリング
- プロセスの切替(コンテキスト切り替え)
- 軽いプロセス(以下では軽量プロセス)
この実験では、普通のプロセス(regular process)と軽量プロセス
(lightweight process)という2種類のプロセスを用いる。両者の
間に基本的な考え方は共通している。たとえば、状態遷移、
スケジューリング、プロセスの切り替え(コンテキストの切り替え)
などの考え方や仕組みは、両者で共通である。両者の違いを以下に
まとめる。
| プロセス | 軽量プロセス |
単位 | プログラム | 手続き(関数) |
保護 | あり | なし |
操作 | 重たい | 軽い |
軽量プロセスは、普通のプロセスの中に作られる小型のプロセスである。
普通のプロセスを生成する場合、実行形式が格納されたファイルを指定する。
一方、軽量プロセスを生成する場合、手続き(C言語の関数)を指定する。
普通のプロセスでは、保護の機能が働き、1つのプロセスが暴走しても
システム全体が停止することはない。一方軽量プロセスでは、一つの
軽量プロセスでエラーが発生し実行が続けられなくなると、プロセス全体が
以上終了させられる。軽量プロセスの操作(プロセス生成、プロセス間の
同期・通信、コンテキスト切り替え)は、普通のプロセスと比較して、10
倍から100倍程高速に行われる。
---実験
この実験で用いるプログラムは、/net/open/classes/slab/info1/5-process
にある。次のように、各自、自分のホーム・ディレクトリの下にこの実験
用のディレクトリを作りなさない。そして、上のディレクトリにあるファイルを
コピーしなさない。
ここ にもあります。
% mkdir ~/実験用ディレクトリ
% cd ~/実験用ディレクトリ
% cp -r /net/open/classes/slab/info1/5-process/* .
ここで、「実験用ディレクトリ」には、各自都合の良い名前を付けなさい。
他の実験と混ざらないよう、実験テーマごとにディレクトリを作りなさい。
そうしなければ、Makefileなどの同じ名前のファイルが上書きされ
不都合が起きる可能性がある。
---報告書
それぞれの実験に付いて、
作成したプログラムへのポインタ (file server naha (/net/home/y99/j99xxxx )
にコピーして、その場所を書く)、
その説明(説明に必要なソースのコピーは最小限にすること)
(Makefileについても説明すること)、
および、
その実行結果を付けなさい。
この実験では、プログラムの説明では、フローチャートを付加する
必要はない。開発環境と実行環境 (計算機、オペレーティング・
システムのバージョン、コンパイラ)を載せなさい。それぞれの
実験について、プログラム作成に要した時間を書きなさい。
---一般的な注意
報告書は、日本語または英語で記述すること。プログラム、
表、図、数式の羅列は、報告書とは認めない。図は必ず
本文から参照すること。数学における証明のように、
示すべき結論、用いる仮定と前提、推論の詳細について
論理的に記述しなさい。
--Unixのプロセスの観察と簡単な操作
--課題1 コマンドによるプロセスの観察
デスクトップ(pw)またはノートPC(nw)からloginする。2つ以上の
ウィンドウを開く。一つのウィンドウから ps u コマンドを打ちなさい。
%pw055 ps u
この結果、どのようなプロセスが見えるか。次に、片方のウィンドウで、
次のようにcatコマンドを実行する。
%pw055 cat
<キーボードからの入力待ち>
もう一つの端末から"ps u"コマンドを打ちなさい。
%pw055 ps u
この結果、どのようなプロセスが見えるか。ps コマンドの出力から
それぞれのプロセスの状態、メモリの使用量、プロセスが実行している
プログラムについて
説明しなさい。tcsh ( または、あなたのlogin shell)という表示が
2行以上見つかるはずである。それより、プロセスとプログラムの違い
について考察しなさい。
nirai, kanai などのSolaris は、System V と呼ばれるOSの種類である。
Solaris のps には2種類ある。一つは、/usr/ucb/ps であり、もう一つ
は/bin/psである。この二つの違いは、ある意味でAT&T System V と
(University of California Berkrey) BSD の差を象徴している。
時間があったら二つのps の違いについて調べて見よう。
--課題2 ps aux コマンドによるプロセスの観察
ps aux コマンドを使って、その計算機で動作している全てのプロセス
を調べなさい。何個のプロセスが動作しているか。(niraiとkanai
についても調べて見よう) そのうちのいくつかを選んで、どんな
プロセスがあるかを説明しなさい。レポートは、ps axu の実行結果の
うち、注目すべきものについてのみ抜粋して載せなさい。
(ps agx というのも使われるが、この場合はどうか?)
プロセスを見るコマンドには、top というものもある。top
の動作を見て、プロセスの生成削除の挙動について観察
せよ。
--課題3 コマンドによるプロセスの消去
catコマンドから作られたプロセスを、^C により殺しなさい。また、
kill コマンドによって殺しなさい。
---killコマンドの簡単な使い方
killコマンドは、プロセスに対してシグナルを送るためのコマンドである。
シグナルとは、UNIXの用語で、ソフトウェア割り込みを意味する。シグナル
を受け取ったプロセスは、何の設定もしてない場合は、終了する。
killコマンドは次のような形式で使われる。
% kill プロセス番号
% kill -KILL プロセス番号
ここで -KILL はシグナルの種類をあらわす。-KILL は、プロセスを
必ず終了させるシグナルである。
--課題4 コマンドによるプロセスの一時停止
cat コマンドから作られたプロセスを、^Z により一時停止しなさい。
また、kill コマンドによって一時停止しなさい。(^Zにより
一時停止する時には、誰かがkillコマンドに相当する
signalを出している。いったい誰が出しているのだろう?)
そして、tcsh のfgコマンドを使って実行を再開しなさい。
loop.c
をコンパイルして動かして、別なウィンドウから、
kill -STOP プロセス番号
kill -CONT プロセス番号
kill -KILL プロセス番号
などとしてみよう。
--課題5 ps コマンドの利用法
ps コマンドの次の3つの結果を比較しなさい。
- ps
- ps u
- ps l
表示されるプロセスの違いと、プロセスについての情報の違いについて
述べなさい。
--課題6 ps コマンドによるプロセスの優先順度の観察
ps alx コマンドを使って、その計算機で動作している全てのプロセスを
調べなさい。優先順位の高いプロセスは、どれか。
--課題6.5
CPU負荷の高い簡単なプログラムを作成して、それを複数動かし、
CPUの温度変化、FANの回転数の変化を観測せよ。
CPUの温度変化、FANの回転数の変化を調べるコマンドを探し、
実際の変化を数値で示せ。
--課題7 他人のプロセス
killコマンドで他人のプロセスを殺そうとするとどうなるか調べなさい
--課題8 その他のsignal
topの中からでもkillコマンドを使用することができる。man signal
で表示されるsiganlの役割を実際にsignalを送りながら確認して見よ。
--プロセスの生成
次のように、process-create プログラムをコンパイルし、実行してみなさい。
% make process-create
% ./process-create
%
この結果、/usr/bin/calが実行される。そして、1998年5月のカレンダが
表示される。
% ./process-create
May 1998
S M Tu W Th F S
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
%
process-create.c の内容は
ここ
である。
main()
{
/*
process_create_l() は、第1引数としてプログラムが格納されているファイ
ルを指定する。それ以降の引数は、新たに生成されるプロセスに渡される。
*/
process_create_l("/usr/bin/cal","cal","5","1998",0 );
}
process_create_1は、第一引数としてプログラムが格納されている
ファイルを指定する。それ以降の引数は、新たに生成されるプロセス
に引き渡される引数である。
process_create_l( file,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9 )
char *file,*a0,*a1,*a2,*a3,*a4,*a5,*a6,*a7,*a8,*a9 ;
{
int child_pid ;
if( (child_pid=fork()) == 0 )
{
char *argv[11] ;
argv[0] = a0 ;
argv[1] = a1 ;
argv[2] = a2 ;
argv[3] = a3 ;
argv[4] = a4 ;
argv[5] = a5 ;
argv[6] = a6 ;
argv[7] = a7 ;
argv[8] = a8 ;
argv[9] = a9 ;
argv[10] = 0 ;
execve( file, argv, environ );
perror( file );
exit( 1 );
}
else
{
return( child_pid );
}
}
この課題では、 UNIX
では、プログラムからプロセスを生成するため、fork()とexecve()という
二つのシステム・コールを使うことに注意しなさい。fork()は、
プロセスのコピーを行うシステム・コール、execve()は、実行している
プログラムを切り替えるシステム・コールである。
--課題8 実行形式プログラムからのプロセス生成
プログラムprocess-create.cを書き換えて、cal以外のプログラムから
2つのプロセスを作るプログラムを作りなさない。ここでは、プロセス
を生成するために使うプログラムとしては、次のようなキーボードからの
入力を行わないものを選びなさい。
- ls -l
- who
- finger user
- cat filename
自分で作ったプログラムを使う時には、キーボード(標準入力)からの
入力を行わないものを使いなさない。 ( 入力を行うものを使うと、
どうなるのだろう? 課題9 のwait を使うと、入力を行うコマンド
でも、ちゃんと動作するようになる。これは何故だろうか? )
UNIXのシェルは、利用者から受け取った文字列から、実行すべきプログラムを
探し、引数を整えて、プロセスを生成する。シェルがプロセスを生成する時には、
内部的には、process_create_1()と似た仕掛けを利用している。
process_create_l()やその内部で利用しているexecvc()システム・コールは、
実行形式が格納されているファイルの絶対パス名、またはcurrent working
directory からの相対パス名を必要とする。すなわち、実行形式が格納されている
ファイル名がスラッシュ(/)から始まる場合は絶対パス名として、ドット(.)
から始まる場合は、相対パス名として解釈される。
シェルは、環境変数PATH(tcshのシェル変数pathと連動している)に含まれている
ディレクトリにあるコマンドを検索し実行する機能がある。この時、どのような
仕掛けを利用しているかを考察しなさい。(tcshのrehashコマンドとも関係が
あるので、tcshのドキュメントを調べて見よう)
ここで「キーボードからの入力を行わないプログラム」というのは、シェル
から実行した時に、キーボードを打たなくても自動的に終了するようなもの
である。自動的に終了しない、「キーボードからの入力を行うプログラム」
の例としては、emacs, less, mnews がある。このようなキーボードからの
入力を受け付けて動くようなプログラムを、「対話的なプログラム」という。
(正確には、cat なども入力を受け付けるが、これは emacs などとは少し
状況が違う。キーボードの入力を即時にしようするか、一旦、バッファに
ためて使用するかという差がある。)
---課題9 wait()システム・コールの利用
process-create は、入力を必要とするようなコマンドでは問題がある。また、
次のように表示が乱れることがある。
% ./process-create
% May 1998
S M Tu W Th F S
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
この図のように、シェルのプロンプト(%)の後に、calコマンドの結果が
表示されている。この理由は、process-createが、子供のcalプロセスの終了を
またずに終了しているからである。この問題を、wait()システム・コール
を用いて解決しなさい。( man 2 wait とすると英語のマニュアルを読むことが
できる)
ある対話的なプログラムのプロセスの中から別の対話的なプログラムのプロセス
を起動しようとすると、元のプロセスと新しいプロセスの間でキーボードから
来るデータの奪い合いが生じる。このキーボードからのデータを、どのプロセ
スに送ったら良いかを管理するしかけは、UNIXでは、suspend などと同様にジ
ョブ制御機能の一部である。tcsh は、& を付けないで実行されたコマン
ドのプロセスに対してキーボードからの入力を送るように設定する。
tcshのfgは、キーボードからの入力を指定されたプロセスに送るように
し、指定されたプロセスをsuspend状態からactiveな状態へ変更するコマンド
である。
元のプロセスと新しいプロセスの間でキーボードから来るデータの奪い合い
を調停する方法の一つは、子供のプロセスが動いている間、元のプロセス
を停止させることである。これを実現するには課題9で用いるwait()システム・
コールを使えば良い。この仕掛けは、mnews などで記事を書く時にエディタ
(mule/emacs)を実行する時に使われている。
---課題10 process_create_l()関数の改良
process-create.c のprocess_create_1()は、最大10個までしか
引数を取ることができない。この制限を、
stdarg マクロを用いて緩和しなさい。例えば、1000個まで
受け付けられるように書き換えなさない。( 実際には、Unix
kernel でこの上限は決まっている。これは、どこで決まっている
のだろうか?)
もちろん、man stdarg を使うこと。
---課題11 Java によるプロセス生成
以下は、Java を使ったプロセス生成である。
processExample.Create
package processExample;
import java.io.*;
public class Create {
static final int BUFSIZE = 4096;
static public void main(String [] args) throws IOException {
Process p = new ProcessBuilder("/usr/bin/cal", "5","2006").start();
InputStream out = p.getInputStream();
byte[] buf = new byte[BUFSIZE];
int length;
while((length=out.read(buf))>0) {
System.out.write(buf,0,length);
}
}
}
(Java 1.4 以前では、ProcessBuilder ではなくて、Runtime.getRuntime() を
使う)
課題8, 9 に相当する修正を行い、実行を確認せよ。確認は、Junit 4 を使って下さい。
while文中の操作がが必要な理由を、Java API document の記述を
指摘して示せ。