軽量プロセスの生成

Menu Menu

ソースの場所

ここ

にあります。


軽量プロセスと手続きの比較

次のようにして、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 <stdio.h>
    #include <pthread.h>

軽量プロセスを利用するプログラムは、このヘッダを読み込む。この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を変更して実行がどのようになるかを観察せよ。


Shinji KONO / Tue Dec 18 12:39:44 2007