5 プロセスの概念とその基本操作

プロセスの生成

次のように、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つのプロセスを作るプログラムを作りなさない。ここでは、プロセス を生成するために使うプログラムとしては、次のようなキーボードからの 入力を行わないものを選びなさい。
  1. ls -l
  2. who
  3. finger user
  4. 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個までしか 引数を取ることができない。この制限を、varargs, または stdarg マクロを用いて緩和しなさい。例えば、1000個まで 受け付けられるように書き換えなさない。( 実際には、Unix kernel でこの上限は決まっている。これは、どこで決まっている のだろうか?)