オペレーティングシステム II 電子・情報工学系 新城 靖 (臨時代講) <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/os2-2002/2003-02-28
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
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-2002/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: 15: extern void echo_server( int portno ); 16: extern void echo_reply( int com ); 17: extern void print_host_port( int portno ); 18: extern void tcp_peeraddr_print( int com ); 19: extern tcp_acc_port( int portno ); 20: extern void delete_zombie(void); 21: extern ssize_t writen(int fd, const void *vptr, size_t n); 22: extern ssize_t readline(int fd, void *vptr, size_t maxlen); 23: 24: main( int argc, char *argv[] ) 25: { 26: int portno ; 27: if( argc >= 3 ) 28: { 29: fprintf( stdout,"Usage: %s [portno] \n",argv[0] ); 30: exit( -1 ); 31: } 32: if( argc == 2 ) 33: portno = atoi( argv[1] ); 34: else 35: portno = getuid(); 36: echo_server( portno ); 37: } 38: 39: void 40: echo_server( int portno ) 41: { 42: int acc,com ; 43: pid_t child_pid ; 44: acc = tcp_acc_port( portno ); 45: if( acc<0 ) 46: exit( -1 ); 47: print_host_port( portno ); 48: while( 1 ) 49: { 50: delete_zombie(); 51: if( (com = accept( acc,0,0 )) < 0 ) 52: { 53: perror("accept"); 54: exit( -1 ); 55: } 56: tcp_peeraddr_print( com ); 57: if( (child_pid=fork()) > 0 ) /* parent */ 58: { 59: close( com ); 60: } 61: else if( child_pid == 0 ) /* parent */ 62: { 63: close( acc ); 64: echo_reply( com ); 65: printf("[%d] connection (fd==%d) closed.\n",getpid(),com ); 66: close( com ); 67: exit( 0 ); 68: } 69: else 70: { 71: perror("fork"); 72: exit( -1 ); 73: } 74: } 75: } 76: 77: #define BUFFERSIZE 1024 78: 79: void 80: echo_reply( int com ) 81: { 82: char line[BUFFERSIZE] ; 83: int rcount ; 84: int wcount ; 85: 86: while( (rcount=readline(com,line,BUFFERSIZE)) > 0 ) 87: { 88: printf("[%d] read(%d,,) %d bytes, %s",getpid(),com,rcount,line ); 89: fflush( stdout ); 90: if( (wcount=writen(com,line,rcount))!= rcount ) 91: { 92: perror("write"); 93: exit( 1 ); 94: } 95: } 96: } ... 99: print_host_port( int portno ) ... 108: tcp_peeraddr_print( int com ) ... 128: tcp_acc_port( int portno ) ... 156: delete_zombie() ... ----------------------------------------------------------------------
サーバ側。サーバは、終了しないので、最後に、^C か Del を押して、割り込みを掛けて終了させる。
PID が違う。---------------------------------------------------------------------- % ./echo-server-fork 1231run telnet adonis9.coins.tsukuba.ac.jp 1231 [30500] connection (fd==4) from 130.158.86.21:40073 [30501] read(4,,) 5 bytes, 012 [30500] connection (fd==4) from 130.158.86.29:36041 [30503] read(4,,) 5 bytes, abc [30503] read(4,,) 5 bytes, def [30503] connection (fd==4) closed. [30501] read(4,,) 5 bytes, 345 [30501] 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-2001/ipc/echo-server-fork.c 5: $Header: /home/lab2/OS/yas/syspro-2001/ipc/RCS/echo-server-pthread.c,v 1.1 2002/02/04 12:27:08 yas Exp $ 6: Start: 2002/02/04 21:17:04 7: */ 8: #include <stdio.h> 9: #include <sys/types.h> /* socket(), time() */ 10: #include <sys/socket.h> /* socket() */ 11: #include <netinet/in.h> /* struct sockaddr_in */ 12: #include <pthread.h> /* pthread_create */ 13: 14: extern void echo_server( int portno ); 15: extern void echo_reply( int com ); 16: extern void print_host_port( int portno ); 17: extern void tcp_peeraddr_print( int com ); 18: extern tcp_acc_port( int portno ); 19: extern ssize_t writen(int fd, const void *vptr, size_t n); 20: extern ssize_t readline(int fd, void *vptr, size_t maxlen); 21: 22: main( int argc, char *argv[] ) 23: { 24: int portno ; 25: if( argc >= 3 ) 26: { 27: fprintf( stdout,"Usage: %s [portno] \n",argv[0] ); 28: exit( -1 ); 29: } 30: if( argc == 2 ) 31: portno = atoi( argv[1] ); 32: else 33: portno = getuid(); 34: echo_server( portno ); 35: } 36: 37: void 38: echo_server( int portno ) 39: { 40: int acc,com ; 41: pthread_t worker ; 42: acc = tcp_acc_port( portno ); 43: if( acc<0 ) 44: exit( -1 ); 45: print_host_port( portno ); 46: while( 1 ) 47: { 48: if( (com = accept( acc,0,0 )) < 0 ) 49: { 50: perror("accept"); 51: exit( -1 ); 52: } 53: tcp_peeraddr_print( com ); 54: if( pthread_create( &worker, NULL, (void *)echo_reply, (void *)com) 55: != 0 ) 56: { 57: perror("pthread_create()"); 58: close( com ); 59: } 60: pthread_detach( worker ); 61: } 62: } 63: 64: #define BUFFERSIZE 1024 65: 66: void 67: echo_reply( int com ) 68: { 69: char line[BUFFERSIZE] ; 70: int rcount ; 71: int wcount ; 72: 73: while( (rcount=readline(com,line,BUFFERSIZE)) > 0 ) 74: { 75: printf("[%d,%d] read() %d bytes, %s",getpid(),com,rcount,line ); 76: fflush( stdout ); 77: if( (wcount=writen(com,line,rcount))!= rcount ) 78: { 79: perror("write"); 80: exit( 1 ); 81: } 82: } 83: printf("[%d,%d] connection closed.\n",getpid(),com ); 84: close( com ); 85: } ----------------------------------------------------------------------特徴
---------------------------------------------------------------------- % 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 [12202] connection (fd==4) from 130.158.86.29:49713 [12264] read(4,,) 5 bytes, 123 [12202] connection (fd==7) from 127.0.0.1:49714 [12266] read(7,,) 5 bytes, abc [12266] read(7,,) 5 bytes, def [12264] read(4,,) 5 bytes, 345 [12266] connection (fd==7) closed. [12264] 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 adonis1. 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-2002/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() で書ける。