-title: 軽量プロセスの生成
ソースの場所
ここ
にあります。
--軽量プロセスと手続きの比較
次のようにして、mp-create と function-call
プログラムをコンパイルし、実行してみなさい。
% make mp-create
% ./mp-create
% make function-call
% ./function-call
%
この二つのプログラムの動きの違いから、軽量プロセスと手続き(C言語
の関数)の違いについて考察しなさい。
mp-create.c のプログラムを以下に示す。
/*
mp-create.c -- 軽量プロセスの生成
$Header: /net/home/cvs/teacher/kono/os/ex/thread/thread.ind,v 1.1.1.1 2007/12/18 03:39:44 kono Exp $
Start: 1995/02/27 16:42:19
*/
#include
#include
軽量プロセスを利用するプログラムは、このヘッダを読み込む。このpthread
は、UnixのPOSIX, X/Openによる標準機能であ。pthread は Linux および
Mac OS Xで利用することができる。しかし、Solaris はkernel threadであり、
Mac OS Xは、user level thread である。
軽量プロセスとして用いる関数は、利用する前に宣言、あるいは本体の定義を
しておく必要がある。
void *main_0(void*);
void *process_A(void*);
void *process_B(void*);
void *process_C(void*);
void ready_print();
このpthreadではready_printというのは、あまりたいしたことができない。
pthread の情報格納する領域をthreadごとに用意する。また、引数は
配列という形で提供しなければならないので、それも別に用意する。
pthread_t thread_main,thread_a,thread_b,thread_c;
int arg_a[]={0,1,2};
int arg_b[]={1,3,5};
int arg_c[]={2,3,5};
pmpthread_create() は、軽量プロセスを作る手続きである。第1引数は、
pthreadの情報である。次の引数には、pthreadの属性を指定することが
できるが、ここではNULLを指定している。第3引数が、
もとになる
関数へのポインタである。第4引数は、その関数を呼び出す時に引き渡される
引数である。ここでも引数は指定していない。
int
main(int ac,char *av[])
{
pthread_create(&thread_main, NULL, &main_0, NULL);
pthread_join(thread_main, NULL);
}
ここでは、ただ一つのpthreadを生成している。そして、最後に、pthread_join
を使って、このthread_main の終了を待つ必要がある。これは、普通のプロセ
スのwaitに相当する。main では、次に3つの軽量プロセスを生成する。
void *
main_0(void *arg)
{
fprintf(stderr,"creating process_A() ...\n");
/* 軽量プロセスの生成 */
pthread_create(&thread_a, NULL, &process_A, arg_a);
fprintf(stderr,"done.\n");
fprintf(stderr,"creating process_B() ...\n");
/* 軽量プロセスの生成 */
pthread_create(&thread_b, NULL, &process_B, arg_b);
fprintf(stderr,"done.\n");
fprintf(stderr,"creating process_C() ...\n");
/* 軽量プロセスの生成 */
pthread_create(&thread_c, NULL, &process_C, arg_c);
fprintf(stderr,"done.\n");
次に ready_print() としているが残念ながら、あまり有用な情報は
得られない。良く観察して何が起きているかを考察すること。これらの
thread は終了を待つ必要がある。
pthread_join(thread_a, NULL);
pthread_join(thread_b, NULL);
pthread_join(thread_c, NULL);
この時点でCPU資源の待ち行列には、いくつのプロセスがどのような順番で
並んでいるかを観察しなさい。
void *
process_A( void *arg )
{
int x ;
int *a = arg;
fprintf(stderr,"process_A( %d,%d,%d ) created.\n", a[0],a[1],a[2]);
fprintf(stderr,"&x == 0x%x\n", &x );
ready_print(); /* CPU資源の待ち行列の表示 */
fprintf(stderr,"process_A() exit.\n");
}
process_A,process_B,process_C の終了をpthread_joinで確認すると、
CPU資源の待ち行列は、空になる。全ての軽量プロセスが終了す
ると、mainのthreadの終了を確認してプロセス全体が終了する。
(本当は、この実験では特にmainのtreadは必要ない)
ready_print()では、単に自分のthread情報のアドレスを表示する。
本来は、ここでthreadのqueueを表示するべきだろう。
このプログラムと、function-call.c の手続き呼び出しと比較
してみよ。
このプログラムは、LinuxとMac OS X上で動作する。
Linux上とMac OS X上の両方での動作を確認してみよ。
--プロセスのコンテキスト切り替えの観察
次のようにして、mp-yieldをコンパイルし、実行してコンテキスト切り替え
の様子を観察しなさい。
% make mp-yield
% ./mp-yield
%
この結果より、2つの軽量プロセスが交互に動作していることがわかる。
動作が切り替わる切っ掛けとなっているものが、sched_yield()である。
(Solaris では名称が異なる)
sched_yield() は、現在実行中の軽量プロセスが、利用しているCPU
資源を自発的に他の軽量プロセスに譲る命令である。sched_yield()を
呼び出した結果、現在実行中の軽量プロセスは、実行可能状態となる。
制御はスケジューラに移る。スケジューラは、CPU資源の待ち行列から、
実行可能状態の軽量プロセスを選び、その軽量プロセスに制御を移す。
--3つのプロセスの間のコンテキスト切り替え
課題のプログラムを変更して、3つのプロセスの間で制御が入れ替わる
ようなプログラムを書きなさい。
--FIFOスケジューリングの観察
次のようにして、mp-wakeupをコンパイルし、実行してみなさい。
% make mp-wakeup
% ./make-wakeup
%
この軽量プロセス・システムでは、FIFO (First In First Out,FCFS (First
come first serveともいう) スケジューリングを行っている。このことを、
この mp-wakeup-.c と課題11 の mp-create.c の動きの違いから説明しなさない。
以下に mp-wakeup.c を示す。
int
main(int ac,char *av[])
{
pthread_attr_init(&attr_main);
attr_main.schedparam.sched_priority = 100;
pthread_create(&thread_main, &attr_main, &main_0, NULL);
pthread_join(thread_main, NULL);
}
ここではpthread_create()の時に属性としてpriorityを指定している。
このように直接構造体を指定する方法は若干ポータビリティが低い。
このpriority を変更することによってFIFOスケジューリングを
変更することができる。
void *
main_0(void *arg)
{
fprintf(stderr,"creating process_A() ...\n");
/* 軽量プロセスの生成 */
pthread_attr_init(&attr_a);
attr_a.schedparam.sched_priority = 10;
pthread_create(&thread_a, &attr_a, &process_A, arg_a);
fprintf(stderr,"done.\n");
fprintf(stderr,"creating process_B() ...\n");
/* 軽量プロセスの生成 */
pthread_attr_init(&attr_b);
attr_b.schedparam.sched_priority = 20;
pthread_create(&thread_b, &attr_b, &process_B, arg_b);
fprintf(stderr,"done.\n");
fprintf(stderr,"creating process_C() ...\n");
/* 軽量プロセスの生成 */
pthread_attr_init(&attr_c);
attr_c.schedparam.sched_priority = 30;
pthread_create(&thread_c, &attr_c, &process_C, arg_c);
fprintf(stderr,"done.\n");
このpriorityを変更して実行がどのようになるかを観察せよ。