-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を変更して実行がどのようになるかを観察せよ。