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

図? 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:
実行例。
% cp ~yas/classes/os2-2004/create-join-rnd.c .この例では、次の3つのスレッドが作られる。% 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-nofork-fdopen.c -- 受け取った文字列をそのまま返すサーバ(fork無し版)
4: ~yas/syspro/ipc/echo-server-nofork-fdopen.c
5: Created on 2004/05/09 19:08:47
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 int fdopen_sock( int sock, FILE **inp, FILE **outp );
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 = strtol( argv[1],0,10 );
34: else
35: portno = getuid();
36: echo_server( portno );
37: }
38:
39: void
40: echo_server( int portno )
41: {
42: int acc,com ;
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: echo_reply( com );
56: }
57: }
58:
59: #define BUFFERSIZE 1024
60:
61: void
62: echo_reply( int com )
63: {
64: char line[BUFFERSIZE] ;
65: int rcount ;
66: int wcount ;
67: FILE *in, *out ;
68:
69: if( fdopen_sock(com,&in,&out) < 0 )
70: {
71: fprintf(stderr,"fdooen()\n");
72: exit( 1 );
73: }
74: while( fgets(line,BUFFERSIZE,in) )
75: {
76: rcount = strlen( line );
77: printf("[%d] received (fd==%d) %d bytes, [%s]\n",getpid(),com,rcount,line );
78: fflush( stdout );
79: fprintf(out,"%s",line );
80: }
81: printf("[%d] connection (fd==%d) closed.\n",getpid(),com );
82: fclose( in );
83: fclose( out );
84: }
85:
...
87: print_my_host_port( int portno )
...
96: tcp_peeraddr_print( int com )
...
112: sockaddr_print( struct sockaddr *addrp, int addr_len )
...
122: tcp_acc_port( int portno )
サーバ側。サーバは、終了しないので、最後に、^C か Del を押して、割り込みを掛けて終了させる。
注意:全員がポート番号 1231 を使うとプログラムが動かないことがある。
% cp ~yas/syspro/ipc/echo-server-nofork-fdopen.c .クライアント側% make echo-server-nofork-fdopen
cc echo-server-nofork-fdopen.c -o echo-server-nofork-fdopen % ./echo-server-nofork-fdopen 1231
run telnet adonis9.coins.tsukuba.ac.jp 1231 [6419] connection (fd==4) from 130.158.86.28:47329 [6419] received (fd==4) 5 bytes, [123 ] [6419] received (fd==4) 5 bytes, [456 ] [6419] received (fd==4) 5 bytes, [790 ] [6419] connection (fd==4) closed. ^C %
![]()
% telnet adonis9.coins.tsukuba.ac.jp 1231複数のクライアントに対してサービスの同時に提供することが望ましい。 echo-server-nofork-fdopen.c は、1つのクライアントからの接続されると、そのクライアントに掛り切りに なって、他のクライアントにはサービスを提供できないという問題がある。 標準の echo サーバ(ポート番号 7 で動作している) は、複数のクライアント から接続されれた場合、同時にサービスを提供することができる。Trying 130.158.86.29... Connected to adonis9.coins.tsukuba.ac.jp. Escape character is '^]'. 123
123 456
456 790
790 ^]
telnet> quit
Connection closed. %
![]()
複数のクライアントに対してサービスの同時に提供するには次のような方法が ある。
1:
2: /*
3: echo-server-pthread.c -- 受け取った文字列をそのまま返すサーバ(pthread版)
4: ~yas/syspro/ipc/echo-server-pthread.c
5: Created on 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 int fdopen_sock( int sock, FILE **inp, FILE **outp );
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 = strtol( argv[1],0,10 );
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_my_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: exit( 1 );
59: }
60: pthread_detach( worker );
61: }
62: }
63:
違い
echo-server-nofork-fdopen.c
55: echo_reply( com );
echo-server-pthread.c
54: if( pthread_create( &worker, NULL, (void *)echo_reply, (void *)com)
55: != 0 )
56: {
57: perror("pthread_create()");
58: exit( 1 );
59: }
60: pthread_detach( worker );
特徴
% ./echo-server-pthread 1231PID が本来は、同じはずである。しかし、Linux では、違う。 ファイル記述子は、同じプロセス内で共有される。 (Solaris (vine1)で試してみなさい。)run telnet adonis9.coins.tsukuba.ac.jp 1231 [7298] connection (fd==4) from 130.158.86.28:47333 [7300] received (fd==4) 5 bytes, [012 ] [7298] connection (fd==8) from 127.0.0.1:42170 [7302] received (fd==8) 5 bytes, [abc ] [7302] received (fd==8) 5 bytes, [def ] [7302] connection (fd==8) closed. [7300] received (fd==4) 5 bytes, [345 ] [7300] connection (fd==4) closed. ^C %
![]()
クライアント側(その1)。
% telnet adonis9.coins.tsukuba.ac.jp 1231クライアント側(その2)。Trying 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: * 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-2004/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 %
![]()
1: /*
2: EchoServerSingle.java -- 文字列を送受信するサーバ(TCP/IP, Java版, スレッドなし)
3: ~yas/syspro/ipc/EchoServer.java
4: Created on 2004/05/09 20:00:24
5: */
6:
7: import java.net.*;
8: import java.io.*;
9:
10: class EchoServerSingle
11: {
12: public static void main(String argv[]) throws IOException {
13: if( argv.length != 1 )
14: {
15: System.err.println("Usage: % java EchoServer port");
16: System.exit( -1 );
17: }
18: int portno = Integer.parseInt( argv[0] );
19: echo_server( portno );
20: }
22: public static void echo_server( int portno ) throws IOException
23: {
24: ServerSocket acc = new ServerSocket( portno );
25: print_my_host_port( portno );
26: while( true )
27: {
28: Socket com = acc.accept();
29: tcp_peeraddr_print( com );
30: EchoServerWorker esw = new EchoServerWorker(com);
31: esw.run();
32: }
33: }
<省略>
1: /*
2: EchoServerWorker.java -- 文字列を送受信するサーバ/ワーカ(TCP/IP, Java版)
3: ~yas/syspro/ipc/EchoServerWorker.java
4: */
5:
6: import java.net.*;
7: import java.io.*;
8:
9: public class EchoServerWorker implements Runnable
10: {
11: Socket com ;
12: EchoServerWorker( Socket com )
13: {
14: this.com = com ;
15: }
16: public void run()
17: {
18: try
19: {
20: BufferedReader in = new BufferedReader(
21: new InputStreamReader( com.getInputStream() ));
22: PrintStream out = new PrintStream( com.getOutputStream() );
23: String line;
24: while( (line = in.readLine())!= null )
25: {
26: stdout.println("received (hash=="+com.hashCode()+") "+
27: line.length()+" characters, ["+line+"]");
28: out.println( line );
29: }
30: stdout.println("connection (hash=="+com.hashCode()+") closed.");
31: in.close();
32: out.close();
33: com.close();
34: }
35: catch( IOException e )
36: {
37: stderr.println( e );
38: }
39: }
40: static java.io.BufferedReader stdin =
41: new java.io.BufferedReader( new java.io.InputStreamReader(System.in) );
42: static java.io.PrintStream stdout = System.out;
43: static java.io.PrintStream stderr = System.err;
44: }
1: /*
2: EchoServer.java -- 文字列を送受信するサーバ(TCP/IP, Java版)
3: ~yas/syspro/ipc/EchoServer.java
4: Created on 2004/02/14 16:22:13
5: */
6:
7: import java.net.*;
8: import java.io.*;
9:
10: class EchoServer
11: {
12: public static void main(String argv[]) throws IOException {
13: if( argv.length != 1 )
14: {
15: System.err.println("Usage: % java EchoServer port");
16: System.exit( -1 );
17: }
18: int portno = Integer.parseInt( argv[0] );
19: echo_server( portno );
20: }
21:
22: public static void echo_server( int portno ) throws IOException
23: {
24: ServerSocket acc = new ServerSocket( portno );
25: print_my_host_port( portno );
26: while( true )
27: {
28: Socket com = acc.accept();
29: tcp_peeraddr_print( com );
30: Thread th = new Thread( new EchoServerWorker(com) );
31: th.start();
32: }
33: }
<省略>
違いは、次の部分だけである。
EchoServerSingle.java 30: EchoServerWorker esw = new EchoServerWorker(com); 31: esw.run(); EchoServer.java 30: Thread th = new Thread( new EchoServerWorker(com) ); 31: th.start();
<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 に対応していないブラウザのためのテキストを書く。
定期的に repaint() メソッドを呼ぶには、アプレットの start() メソッドで スレッドを生成(new)し、そのスレッドの start() メソッドを呼び、実行する。
スレッドは、run() メソッドを呼ぶ。run() メソッドの中で、repaint() メソッ ドを呼ぶ。
1: //
2: // rctext.java -- rolling color text demo by Y.Shinjo <yas@is.tsukuba.ac.jp>
3: // Created on1997/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: x += step ;
38: if( x > getSize().width )
39: {
40: FontMetrics fm = g.getFontMetrics();
41: x = - fm.stringWidth( message );
42: }
43: y += step ;
44: if( y > getSize().height + fontsize )
45: {
46: y = 0 ;
47: n ++ ;
48: if( n>= color.length )
49: n = 0 ;
50: }
51: g.setFont( font );
52: g.setColor( color[n] );
53: g.drawString( message, x, y );
54: }
55:
56: public void start()
57: {
58: thread = new Thread(this);
59: thread.start();
60: }
61:
62: public void stop()
63: {
64: thread = null;
65: }
66:
67: public void run()
68: {
69: Thread thisThread = Thread.currentThread();
70: while( thread == thisThread )
71: {
72: try
73: {
74: thisThread.sleep( sleep ) ;
75: }
76: catch( InterruptedException e )
77: {
78: stop();
79: }
80: repaint();
81: }
82: }
83: }
SCHED_OTHER
SCHED_FIFO
SCHED_RR
(実時間スレッドの)優先順位の設定
優先順位の低いスレッドが確保したロックを優先順位が高いスレッドが確保す るのを試みてブロックされる。
スレッドの状態
優先順位
Thread.MAX_PRIORITY
Thread.MIN_PRIORITY
Thread.NORM_PRIORITY
優先順位の設定
スケジューリングが行われる(スレッドが切替えられる可能性がある)イベント
スレッドを殺したり外から強制的に止めたりすると、地獄に落ちる。
suspend(), resume() は、清く正しいプログラムには不要である。 多くの場合、wait(), notify() で書ける。