オペレーティングシステム II
                                       電子・情報工学系
                                       新城 靖 (臨時代講)
                                       <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.coins.tsukuba.ac.jp/~yas/classes/os2-2003/2004-01-13
あるいは、次のページから手繰っていくこともできます。
	http://www.coins.tsukuba.ac.jp/~yas/classes/
	http://www.is.tsukuba.ac.jp/~yas/index-j.html
軽量プロセスというと、内部にループを含むような語感がある。
サブルーチンと比較可能。
Pthread を利用したプログラムを書く時には、次のようなヘッダ・ファイルを 読み込む。
#include <pthread.h>
SGI (O2, Origin), Linux でリンク。-lpthread を付ける。
FreeBSD でのリンク。---------------------------------------------------------------------- % cc -o create create.c -lpthread% ./create
... %
----------------------------------------------------------------------
-lc_r を付ける。
---------------------------------------------------------------------- % cc -o create create.c -lc_r% ./create
... %
----------------------------------------------------------------------
Solaris (Unix International系)でのコンパイルとリンク。
-D_REENTRANTと-lpthread を付ける。
セマフォを使う時、SunOS 5.6 では、リンク時に---------------------------------------------------------------------- % cc -D_REENTRANT -o create create.c -lpthread% ./create
... %
----------------------------------------------------------------------
-lposix4 オプショ
ンを付ける。SunOS 5.7, 5.8 では、リンク時に -lrt オプションを付
ける。

図? fork-joinモデルの実現
後で join する必要がない時には、pthread_detach() を使って切り離す。 (joinしなくてもゾンビが残らない。)
----------------------------------------------------------------------
   1: 
   2: /*
   3:         create-join-rnd.c -- スレッドを2つ作るプログラム
   4:         Start: 2002/02/02 20:00:25
   5: */
   6: 
   7: #include <pthread.h>
   8: #include <stdlib.h>     /* lrand48() */
   9: #include <time.h>       /* time() */
  10: #include <sys/types.h>  /* time() */
  11: 
  12: void func1( int x );
  13: void func2( int x );
  14: 
  15: main()
  16: {
  17:     pthread_t t1 ;
  18:     pthread_t t2 ;
  19:         srand48( time(0) );
  20:         pthread_create( &t1, NULL, (void *)func1, (void *)10 );
  21:         pthread_create( &t2, NULL, (void *)func2, (void *)20 );
  22:         printf("main()\n");
  23:         pthread_join( t1, NULL );
  24:         pthread_join( t2, NULL );
  25: }
  26: 
  27: void func1( int x )
  28: {
  29:     int i ;
  30:         for( i = 0 ; i<3 ; i++ )
  31:         {
  32:             sleep_rand();
  33:             printf("func1( %d ): %d \n",x, i );
  34:         }
  35: }
  36: 
  37: void func2( int x )
  38: {
  39:     int i ;
  40:         for( i = 0 ; i<3 ; i++ )
  41:         {
  42:             sleep_rand();
  43:             printf("func2( %d ): %d \n",x, i );
  44:         }
  45: }
  46: 
  47: sleep_rand()
  48: {
  49:     long rnd ;
  50:     struct timespec req ;
  51:         rnd = lrand48();
  52:         req.tv_sec = 0 ;
  53:         req.tv_nsec = rnd ;
  54:         nanosleep( &req, 0 );
  55: }
  56: 
----------------------------------------------------------------------
実行例。
この例では、次の3つのスレッドが作られる。---------------------------------------------------------------------- % cp ~yas/classes/os2-2003/create-join-rnd.c .% gcc -o create-join-rnd create-join-rnd.c -lpthread
% ./create-join-rnd
main() func2( 20 ): 0 func2( 20 ): 1 func2( 20 ): 2 func1( 10 ): 0 func1( 10 ): 1 func1( 10 ): 2 % ./create-join-rnd
main() func1( 10 ): 0 func1( 10 ): 1 func1( 10 ): 2 func2( 20 ): 0 func2( 20 ): 1 func2( 20 ): 2 % ./create-join-rnd
main() func2( 20 ): 0 func1( 10 ): 0 func2( 20 ): 1 func1( 10 ): 1 func1( 10 ): 2 func2( 20 ): 2 %
----------------------------------------------------------------------
func2 から作られたスレッド t2 
func1 から作られたスレッド t1 
pthread_create() により作られる。
どういう順序で実行されるかは、決まっていない。 決まっていない。スレッドは、もともと順番を決めないような処理、 非同期的(asynchronous) な処理を表現するためのもの。どうしても他のスレッドと同期を行なう必要が 出てきた時には、mutex や条件変数といった同期機能を使う。
pthread_create()で指定された関数からリターンすると、そ
のスレッドが終了する。pthread_exit() を呼び出してもよい。
ただし、
初期スレッド
が終了すると、プロセス全体が終了する。
exit() システムコールを呼び出しても終了する。
----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        echo-server-fork.c -- 受け取った文字列をそのまま返すサーバ(fork版)
   4:	        ~yas/syspro/ipc/echo-server-fork.c
   5:	        Start: 1997/06/09 19:46:40
   6:	*/
   7:	#define _USE_BSD        /* wait4() */
   8:	#include <stdio.h>
   9:	#include <sys/types.h>  /* socket(), wait4() */
  10:	#include <sys/socket.h> /* socket() */
  11:	#include <netinet/in.h> /* struct sockaddr_in */
  12:	#include <sys/resource.h> /* wait4() */
  13:	#include <sys/wait.h>   /* wait4() */
  14:	#include <netdb.h>      /* getnameinfo() */
  15:	
  16:	extern  void echo_server( int portno );
  17:	extern  void echo_reply( int com );
  18:	extern  void print_my_host_port( int portno );
  19:	extern  void tcp_peeraddr_print( int com );
  20:	extern  void sockaddr_print( struct sockaddr *addrp, int addr_len );
  21:	extern  tcp_acc_port( int portno );
  22:	extern  void delete_zombie(void);
  23:	extern  ssize_t writen(int fd, const void *vptr, size_t n);
  24:	extern  ssize_t readline(int fd, void *vptr, size_t maxlen);
  25:	
  26:	main( int argc, char *argv[] )
  27:	{
  28:	    int portno ;
  29:	        if( argc >= 3 )
  30:	        {
  31:	            fprintf( stdout,"Usage: %s [portno] \n",argv[0] );
  32:	            exit( -1 );
  33:	        }
  34:	        if( argc == 2 )
  35:	            portno = strtol( argv[1],0,10 );
  36:	        else
  37:	            portno = getuid();
  38:	        echo_server( portno );
  39:	}
  40:	
  41:	void
  42:	echo_server( int portno )
  43:	{
  44:	    int acc,com ;
  45:	    pid_t child_pid ;
  46:	        acc = tcp_acc_port( portno );
  47:	        if( acc<0 )
  48:	            exit( -1 );
  49:	        print_my_host_port( portno );
  50:	        while( 1 )
  51:	        {
  52:	            delete_zombie();
  53:	            if( (com = accept( acc,0,0 )) < 0 )
  54:	            {
  55:	                perror("accept");
  56:	                exit( -1 );
  57:	            }
  58:	            tcp_peeraddr_print( com );
  59:	            if( (child_pid=fork()) > 0 ) /* parent */
  60:	            {
  61:	                close( com );
  62:	            }
  63:	            else if( child_pid == 0 ) /* child */
  64:	            {
  65:	                close( acc );
  66:	                echo_reply( com );
  67:	                printf("[%d] connection (fd==%d) closed.\n",getpid(),com );
  68:	                close( com );
  69:	                exit( 0 );
  70:	            }
  71:	            else
  72:	            {
  73:	                perror("fork");
  74:	                exit( -1 );
  75:	            }  
  76:	        }
  77:	}
  78:	
  79:	#define BUFFERSIZE      1024
  80:	
  81:	void
  82:	echo_reply( int com )
  83:	{
  84:	    char line[BUFFERSIZE] ;
  85:	    int rcount ;
  86:	    int wcount ;
  87:	
  88:	        while( (rcount=readline(com,line,BUFFERSIZE)) > 0 )
  89:	        {
  90:	            printf("[%d] read(%d,,) %d bytes, %s",getpid(),com,rcount,line );
  91:	            fflush( stdout );
  92:	            if( (wcount=writen(com,line,rcount))!= rcount )
  93:	            {
  94:	                 perror("write");
  95:	                 exit( 1 );
  96:	            }
  97:	        }
  98:	}
...
 101:	print_my_host_port( int portno )
...
 110:	tcp_peeraddr_print( int com )
...
 126:	sockaddr_print( struct sockaddr *addrp, int addr_len )
...
 136:	tcp_acc_port( int portno )
...
 168:	delete_zombie()
...
----------------------------------------------------------------------
サーバ側。サーバは、終了しないので、最後に、^C か Del を押して、割り込みを掛けて終了させる。
PID が違う。---------------------------------------------------------------------- % ./echo-server-fork 1231run telnet adonis9.coins.tsukuba.ac.jp 1231 [30566] connection (fd==4) from 130.158.86.71:34091 [30567] read(4,,) 5 bytes, 012 [30566] connection (fd==4) from 130.158.86.29:33984 [30569] read(4,,) 5 bytes, abc [30569] read(4,,) 5 bytes, def [30569] connection (fd==4) closed. [30567] read(4,,) 5 bytes, 345 [30567] connection (fd==4) closed. ^C %
----------------------------------------------------------------------
クライアント側(その1)。
クライアント側(その2)。---------------------------------------------------------------------- % telnet adonis9.coins.tsukuba.ac.jp 1231Trying 130.158.86.29... Connected to adonis9.coins.tsukuba.ac.jp. Escape character is '^]'. 012
012 345
345 ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
---------------------------------------------------------------------- % telnet adonis9.coins.tsukuba.ac.jp 1231Trying 130.158.86.29... Connected to adonis9.coins.tsukuba.ac.jp. Escape character is '^]'. abc
abc def
def ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        echo-server-pthread.c -- 受け取った文字列をそのまま返すサーバ(pthread版)
   4:	        ~yas/syspro/ipc/echo-server-fork.c
   5:	        Start: 2002/02/04 21:17:04
   6:	*/
   7:	#include <stdio.h>
   8:	#include <sys/types.h>  /* socket(), time() */
   9:	#include <sys/socket.h> /* socket() */
  10:	#include <netinet/in.h> /* struct sockaddr_in */
  11:	#include <pthread.h>    /* pthread_create */
  12:	#include <netdb.h>      /* getnameinfo() */
  13:	
  14:	extern  void echo_server( int portno );
  15:	extern  void echo_reply( int com );
  16:	extern  void print_my_host_port( int portno );
  17:	extern  void tcp_peeraddr_print( int com );
  18:	extern  void sockaddr_print( struct sockaddr *addrp, int addr_len );
  19:	extern  tcp_acc_port( int portno );
  20:	extern  ssize_t writen(int fd, const void *vptr, size_t n);
  21:	extern  ssize_t readline(int fd, void *vptr, size_t maxlen);
  22:	
  23:	main( int argc, char *argv[] )
  24:	{
  25:	    int portno ;
  26:	        if( argc >= 3 )
  27:	        {
  28:	            fprintf( stdout,"Usage: %s [portno] \n",argv[0] );
  29:	            exit( -1 );
  30:	        }
  31:	        if( argc == 2 )
  32:	            portno = strtol( argv[1],0,10 );
  33:	        else
  34:	            portno = getuid();
  35:	        echo_server( portno );
  36:	}
  37:	
  38:	void
  39:	echo_server( int portno )
  40:	{
  41:	    int acc,com ;
  42:	    pthread_t worker ;
  43:	        acc = tcp_acc_port( portno );
  44:	        if( acc<0 )
  45:	            exit( -1 );
  46:	        print_my_host_port( portno );
  47:	        while( 1 )
  48:	        {
  49:	            if( (com = accept( acc,0,0 )) < 0 )
  50:	            {
  51:	                perror("accept");
  52:	                exit( -1 );
  53:	            }
  54:	            tcp_peeraddr_print( com );
  55:	            if( pthread_create( &worker, NULL, (void *)echo_reply, (void *)com)
  56:	                != 0 )
  57:	            {
  58:	                perror("pthread_create()");
  59:	                close( com );
  60:	            }
  61:	            pthread_detach( worker );
  62:	        }
  63:	}
  64:	
  65:	#define BUFFERSIZE      1024
  66:	
  67:	void
  68:	echo_reply( int com )
  69:	{
  70:	    char line[BUFFERSIZE] ;
  71:	    int rcount ;
  72:	    int wcount ;
  73:	
  74:	        while( (rcount=readline(com,line,BUFFERSIZE)) > 0 )
  75:	        {
  76:	            printf("[%d] read(%d,,) %d bytes, %s",getpid(),com,rcount,line );
  77:	            fflush( stdout );
  78:	            if( (wcount=writen(com,line,rcount))!= rcount )
  79:	            {
  80:	                 perror("write");
  81:	                 exit( 1 );
  82:	            }
  83:	        }
  84:	        printf("[%d] connection (fd==%d) closed.\n",getpid(),com );
  85:	        close( com );
  86:	}
----------------------------------------------------------------------
特徴
---------------------------------------------------------------------- % cp ~yas/syspro/ipc/echo-server-pthread.c .% cc echo-server-pthread.c -o echo-server-pthread -lpthread
% ./echo-server-pthread 1231
run telnet adonis9.coins.tsukuba.ac.jp 1231 [25710] connection (fd==4) from 130.158.86.29:36267 [25736] read(4,,) 5 bytes, 123 [25710] connection (fd==7) from 127.0.0.1:36269 [25739] read(7,,) 5 bytes, abc [25739] read(7,,) 5 bytes, def [25736] read(4,,) 5 bytes, 345 [25739] connection (fd==7) closed. [25736] connection (fd==4) closed. ^C %
----------------------------------------------------------------------
PID が本来は、同じはずである。しかし、Linux では、違う。 ファイル記述子は、同じプロセス内で共有される。 (Solaris (vine1)で試してみなさい。)
クライアント側(その1)。
クライアント側(その2)。---------------------------------------------------------------------- % telnet adonis9.coins.tsukuba.ac.jp 1231Trying 130.158.86.29... Connected to adonis9.coins.tsukuba.ac.jp. Connected to adonis9. Escape character is '^]'. 012
012 345
345 ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
---------------------------------------------------------------------- % telnet localhost 1231Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. abc
abc def
def ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
SCHED_OTHER
SCHED_FIFO
SCHED_RR
(実時間スレッドの)優先順位の設定
優先順位の低いスレッドが確保したロックを優先順位が高いスレッドが確保す るのを試みてブロックされる。
----------------------------------------------------------------------
   1: 
   2: /*
   3:  * ThreadCreateJoin.java -- スレッドを2つ作るプログラム
   4:  * Start: 2002/02/04 22:08:12
   5: */
   6: 
   7: class ThreadA implements Runnable
   8: {
   9:     int x ;
  10:     java.util.Random rand ;
  11:     ThreadA( int arg_x )
  12:     {
  13:         x = arg_x ;
  14:         rand = new java.util.Random();
  15:     }
  16:     public void run()
  17:     {
  18:         int i ;
  19:         for( i=0 ; i<3 ; i++ )
  20:         {
  21:             sleep_rand();
  22:             System.out.println("ThreadA("+x+").run(): "+i);
  23:         }
  24:     }
  25:     void sleep_rand()
  26:     {
  27:         int sleep = rand.nextInt() % 1000 ;
  28:         if( sleep < 0 ) sleep = -sleep ;
  29:         Thread t1 = Thread.currentThread();
  30:         try
  31:         {
  32:             t1.sleep( sleep );
  33:         }
  34:         catch( java.lang.InterruptedException e )
  35:         {
  36:         }
  37:     }
  38: }
  39: 
  40: class ThreadB implements Runnable
  41: {
  42:     int x ;
  43:     java.util.Random rand ;
  44:     ThreadB( int arg_x )
  45:     {
  46:         x = arg_x ;
  47:         rand = new java.util.Random();
  48:     }
  49:     public void run()
  50:     {
  51:         int i ;
  52:         for( i=0 ; i<3 ; i++ )
  53:         {
  54:             sleep_rand();
  55:             System.out.println("ThreadB("+x+").run(): "+i);
  56:         }
  57:     }
  58:     void sleep_rand()
  59:     {
  60:         int sleep = rand.nextInt() % 1000 ;
  61:         if( sleep < 0 ) sleep = -sleep ;
  62:         Thread t1 = Thread.currentThread();
  63:         try
  64:         {
  65:             t1.sleep( sleep );
  66:         }
  67:         catch( java.lang.InterruptedException e )
  68:         {
  69:         }
  70:     }
  71: }
  72: 
  73: class ThreadCreateJoin
  74: {
  75:     static void main(String argv[])
  76:     {
  77:         Thread t1 = new Thread( new ThreadA(10) );
  78:         t1.start();
  79:         Thread t2 = new Thread( new ThreadB(10) );
  80:         t2.start();
  81:         System.out.println("main()");
  82:         try
  83:         {
  84:             t1.join();
  85:             t2.join();
  86:         }
  87:         catch( InterruptedException e )
  88:         {
  89:             System.err.println("main(): Interrupted");
  90:         }
  91:     }
  92: }
----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % cp ~yas/classes/os2-2003/ThreadCreateJoin.java .% javac ThreadCreateJoin.java
% java ThreadCreateJoin
main() ThreadA(10).run(): 0 ThreadB(10).run(): 0 ThreadB(10).run(): 1 ThreadA(10).run(): 1 ThreadA(10).run(): 2 ThreadB(10).run(): 2 % java ThreadCreateJoin
main() ThreadA(10).run(): 0 ThreadB(10).run(): 0 ThreadB(10).run(): 1 ThreadA(10).run(): 1 ThreadA(10).run(): 2 ThreadB(10).run(): 2 % java ThreadCreateJoin
main() ThreadA(10).run(): 0 ThreadA(10).run(): 1 ThreadB(10).run(): 0 ThreadA(10).run(): 2 ThreadB(10).run(): 1 ThreadB(10).run(): 2 %
----------------------------------------------------------------------
<APPLET></APPLET>。
HTML記述:
<APPLET CODE="rctext.class" WIDTH="500" HEIGHT="50"> <PARAM NAME="message" VALUE="hello,world"> 与えたメッセージが動き回るJavaアプレット。 </APPLET>表示例:
CODE属性で、Javaアプレットを保存している
ファイルのURL(ただし、同一ホスト内)、
WIDTH属性で、幅、
HEIGHT属性で、高さを指定する。
<PARAM>タグを使えば、アプレットに
オプションを渡すことがでる。
<APPLET>と
</APPLET>の間には、
Java Applet に対応していないブラウザのためのテキストを書く。
----------------------------------------------------------------------
   1: //
   2: //  rctext.java -- rolling color text demo by Y.Shinjo <yas@is.tsukuba.ac.jp>
   3: //  Start: 1997/06/18 17:45:00
   4: //
   5: 
   6: import java.awt.* ;
   7: 
   8: public class rctext extends java.applet.Applet implements Runnable 
   9: {
  10:     volatile Thread thread ;
  11:     int fontsize = 24 ;
  12:     Font font ;
  13:     Color color[] ;
  14:     int sleep = 100 ;
  15:     int step = 5 ;
  16:     int x, y, n ;
  17:     String message ;
  18: 
  19:     public void init()
  20:     {
  21:         font = new java.awt.Font("TimesRoman",Font.PLAIN, fontsize );
  22:         message = getParameter("message");
  23:         if( message == null )
  24:             message="null" ;
  25:         x = 0 ; y = 0 ; n = 0 ;
  26:         color = new Color[6] ;
  27:         color[0] = Color.green ;
  28:         color[1] = Color.yellow ;
  29:         color[2] = Color.orange ;
  30:         color[3] = Color.red ;
  31:         color[4] = Color.magenta ;
  32:         color[5] = Color.blue ;
  33:     }
  34: 
  35:     public void paint(Graphics g)
  36:     {
  37:         g.setFont( font );
  38:         g.setColor( getBackground() );
  39:         g.drawString( message, x, y );
  40:         x += step ;
  41:         if( x > size().width )
  42:         {
  43:             FontMetrics fm = g.getFontMetrics();
  44:             x = - fm.stringWidth( message );
  45:         }
  46:         y += step ;
  47:         if( y > size().height + fontsize )
  48:         {
  49:             y = 0 ;
  50:             n ++ ;
  51:             if( n>= color.length )
  52:                 n = 0 ;
  53:         }
  54:         g.setColor( color[n] );
  55:         g.drawString( message, x, y );
  56:     }
  57: 
  58:     public void start()
  59:     {
  60:         thread = new Thread(this);
  61:         thread.start();
  62:     }
  63: 
  64:     public void stop()
  65:     {
  66:         thread = null;
  67:     }
  68: 
  69:     public void run()
  70:     {
  71:         Thread thisThread = Thread.currentThread();
  72:         while( thread == thisThread )
  73:         {
  74:             try
  75:             {
  76:                 thisThread.sleep( sleep ) ;
  77:             }
  78:             catch( InterruptedException e )
  79:             {
  80:                 stop();
  81:             }
  82:             repaint();
  83:         }
  84:     }
  85: }
----------------------------------------------------------------------
----------------------------------------------------------------------
    public void start()
    {
	Thread thisThread = Thread.currentThread();
	while( thread == thisThread )
	{
	    try
	    {
		thisThread.sleep( sleep ) ;
	    }
	    catch( InterruptedException e )
	    {
		stop();
	    }
	    repaint();
	}
    }
----------------------------------------------------------------------
start() からリターンしないので、画面には何も表示されない。
スレッドの状態
優先順位
Thread.MAX_PRIORITY
Thread.MIN_PRIORITY
Thread.NORM_PRIORITY
優先順位の設定
スケジューリングが行われる(スレッドが切替えられる可能性がある)イベント
スレッドを殺したり外から強制的に止めたりすると、地獄に落ちる。
suspend(), resume() は、清く正しいプログラムには不要である。 多くの場合、wait(), notify() で書ける。