分散システム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/dsys-1998/1999-01-19
/pthread.html
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
1つのプロセスの中に複数のスレッドを作って動作させることを マルチスレッド という。これに対して,今までのプロセスは,1つのアドレス空間に1つのスレッ ドだけが動いているもので、これを強調する時には, シングルスレッド のプロセスという。
もともとスレッドは、 並列処理(parallel processing)を行う時に使われてきた(共有メモリ型マルチ プロセッサ、Mach)。最近では、もともと内部に並列性を含んでいたプログラ ムを自然に表現するためにも使われる。
Pthread を利用したプログラムを書く時には、次のようなヘッダ・ファイルを 読み込む。
#include <pthread.h>
O2 でのコンパイルとリンク。-lpthread
を付ける。
---------------------------------------------------------------------- % cc -o create create.c -lpthread% ./create
... %
----------------------------------------------------------------------
図? fork-joinモデルの実現
後で join する必要がない時には、pthread_detach() を使って切り離す。 (joinしなくてもゾンビが残らない。)
図? スレッドの生成とjoin
---------------------------------------------------------------------- 1: 2: /* 3: create-join-2.c -- スレッドを2つ作るプログラム 4: このファイルは次の場所にあります。 5: ~yas/dsys/pthread/create-join-2.c 6: cp コマンドでコピーできます。 7: $Header: /home/lab2/OS/yas/dsys-1998/pthread/RCS/create-join-2.c,v 1.1 1999/01/18 11:52:49 yas Exp $ 8: Start: 1999/01/18 20:49:16 9: */ 10: 11: #include <pthread.h> 12: 13: void func1( int x ); 14: void func2( int x ); 15: 16: main() { 17: pthread_t t1 ; 18: pthread_t t2 ; 19: pthread_create( &t1, NULL, (void *)func1, (void *)1 ); 20: pthread_create( &t2, NULL, (void *)func2, (void *)2 ); 21: printf("main()\n"); 22: pthread_join( t1, NULL ); 23: pthread_join( t2, NULL ); 24: } 25: 26: void func1( int x ) { 27: int i ; 28: for( i = 0 ; i<3 ; i++ ) { 29: printf("func1( %d ): %d \n",x, i ); 30: } 31: } 32: 33: void func2( int x ) { 34: int i ; 35: for( i = 0 ; i<3 ; i++ ) { 36: printf("func2( %d ): %d \n",x, i ); 37: } 38: } ----------------------------------------------------------------------実行例。
この例では、次の3つのスレッドが作られる。---------------------------------------------------------------------- % cp ~yas/dsys/pthread/create-join-2.c .% cc -o create-join-2 create-join-2.c -lpthread
% ./create-join-2
main() func1( 1 ): 0 func2( 2 ): 0 func1( 1 ): 1 func2( 2 ): 1 func1( 1 ): 2 func2( 2 ): 2 %
----------------------------------------------------------------------
func2
から作られたスレッド t2
func1
から作られたスレッド t1
pthread_create()
により作られる。
どういう順序で実行されるかは、決まっていない。 決まっていない。スレッドは、もともと順番を決めないような処理、 非同期的(asynchronous) な処理を表現するためのもの。どうしても他のスレッドと同期を行なう必要が 出てきた時には、mutex や条件変数といった同期機能を使う。
pthread_create()
で指定された関数からリターンすると、そ
のスレッドが終了する。pthread_exit()
を呼び出してもよい。
ただし、
初期スレッド
が終了すると、プロセス全体が終了する。
exit()
システムコールを呼び出しても終了する。
システムプログラムI(1学期)の 1998年6月9日の dnl 1998年6月9日の 再掲。
TCP/IP のポート番号 7 (echo) では、受け取ったデータをそのまま返すサー ビスを提供している。以下は、これと同じような機能を提供するサーバである。 複数の接続先(クライアント)の要求を同時に処理するために、クライアント ごとに fork() システム・コールで専用の子プロセスを作っている。
---------------------------------------------------------------------- 1: 2: /* 3: echo-server-fork.c -- 受け取った文字列をそのまま返すサーバ(fork版) 4: ~yas/syspro1/ipc/echo-server-fork.c 5: $Header: /home/lab2/OS/yas/syspro1-1998/ipc/RCS/echo-server-fork.c,v 1.5 1997/06/09 21:28:27 yas Exp $ 6: Start: 1997/06/09 19:46:40 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: 13: extern void echo_server( int portno ); 14: extern void echo_reply( int com ); 15: extern void print_host_port( int portno ); 16: extern void tcp_peeraddr_print( int com ); 17: extern tcp_acc_port( int portno ); 18: extern int writen( int fd, char *buf, int nbytes ); 19: 20: main( int argc, char *argv[] ) 21: { 22: int portno ; 23: if( argc >= 3 ) 24: { 25: fprintf( stdout,"Usage: %s host port\n",argv[0] ); 26: exit( -1 ); 27: } 28: if( argc == 2 ) 29: portno = atoi( argv[1] ); 30: else 31: portno = getuid(); 32: echo_server( portno ); 33: } 34: 35: void echo_server( int portno ) 36: { 37: int acc,com ; 38: pid_t child_pid ; 39: acc = tcp_acc_port( portno ); 40: if( acc<0 ) 41: exit( -1 ); 42: print_host_port( portno ); 43: while( 1 ) 44: { 45: if( (com = accept( acc,0,0 )) < 0 ) 46: { 47: perror("accept"); 48: exit( -1 ); 49: } 50: tcp_peeraddr_print( com ); 51: if( (child_pid=fork()) > 0 ) /* parent */ 52: { 53: close( com ); 54: } 55: else if( child_pid == 0 ) 56: { 57: close( acc ); 58: echo_reply( com ); 59: printf("[%d,%d] connection closed.\n",getpid(),com ); 60: close( com ); 61: exit( 0 ); 62: } 63: } 64: } 65: 66: void echo_reply( int com ) 67: { 68: char buf[BUFSIZ] ; 69: int rcount ; 70: int wcount ; 71: 72: while( (rcount=read(com,buf,BUFSIZ)) > 0 ) 73: { 74: if( (wcount=writen(com,buf,rcount))!= rcount ) 75: { 76: perror("write"); 77: exit( 1 ); 78: } 79: printf("[%d,%d] ",getpid(),com ); 80: fflush( stdout ); 81: write( 1, buf, rcount ); 82: } 83: } <以下省略> 85: void print_host_port( int portno ) ... 93: void tcp_peeraddr_print( int com ) ... 112: tcp_acc_port( int portno ) ... 141: int writen( int fd, char *buf, int nbytes ) ----------------------------------------------------------------------実行例。
サーバ側。サーバは、終了しないので、最後に、^C か Del を押して、割り込みを掛けて終了させる。
クライアント側(その1)。---------------------------------------------------------------------- % ./echo-server-forkrun telnet adonis1 1231 [4535,4] connection from 130.158.86.1:2368 [4539,4] 012 [4535,4] connection from 130.158.86.9:15629 [4542,4] abc [4542,4] def [4542,4] connection closed. [4539,4] 345 [4539,4] connection closed. ^C %
----------------------------------------------------------------------
クライアント側(その2)。---------------------------------------------------------------------- % telnet adonis1 1231Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. 012
012 345
345 ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
---------------------------------------------------------------------- % % telnet adonis1 1231Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. abc
abc def
def ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: 2: /* 3: echo-server-fork.c -- 受け取った文字列をそのまま返すサーバ(pthread版) 4: ~yas/syspro1/ipc/echo-server-fork.c 5: $Header: /home/lab2/OS/yas/dsys-1998/pthread/RCS/echo-server-pthread.c,v 1.1 1999/01/18 12:29:11 yas Exp $ 6: <-- ~yas/syspro1-1998/ipc/RCS/echo-server-fork.c,v 1.5 1997/06/09 21:28:27 yas Exp $ 7: Start: 1997/06/09 19:46:40 8: */ 9: #include <stdio.h> 10: #include <sys/types.h> /* socket(), time() */ 11: #include <sys/socket.h> /* socket() */ 12: #include <netinet/in.h> /* struct sockaddr_in */ 13: #include <pthread.h> 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 int writen( int fd, char *buf, int nbytes ); 21: 22: main( int argc, char *argv[] ) 23: { 24: int portno ; 25: if( argc >= 3 ) 26: { 27: fprintf( stdout,"Usage: %s host port\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 echo_server( int portno ) 38: { 39: int acc,com ; 40: pthread_t worker ; 41: acc = tcp_acc_port( portno ); 42: if( acc<0 ) 43: exit( -1 ); 44: print_host_port( portno ); 45: while( 1 ) 46: { 47: if( (com = accept( acc,0,0 )) < 0 ) 48: { 49: perror("accept"); 50: exit( -1 ); 51: } 52: tcp_peeraddr_print( com ); 53: if( pthread_create( &worker, NULL, (void *)echo_reply, (void *)com) 54: != 0 ) 55: { 56: perror("pthread_create()"); 57: exit( -1 ); 58: } 59: pthread_detach( worker ); 60: } 61: } 62: 63: void echo_reply( int com ) 64: { 65: char buf[BUFSIZ] ; 66: int rcount ; 67: int wcount ; 68: 69: while( (rcount=read(com,buf,BUFSIZ)) > 0 ) 70: { 71: if( (wcount=writen(com,buf,rcount))!= rcount ) 72: { 73: perror("write"); 74: exit( 1 ); 75: } 76: printf("[%d,%d,%d] ",getpid(),pthread_self(),com ); 77: fflush( stdout ); 78: write( 1, buf, rcount ); 79: } 80: printf("[%d,%d,%d] connection closed.\n",getpid(),pthread_self(),com ); 81: close( com ); 82: } <以下省略> 84: void print_host_port( int portno ) ... 92: void tcp_peeraddr_print( int com ) ... 112: tcp_acc_port( int portno ) ... 141: int writen( int fd, char *buf, int nbytes ) ----------------------------------------------------------------------実行例。
サーバ側。サーバは、終了しないので、最後に、^C か Del を押して、割り込みを掛けて終了させる。
クライアント側(その1)。---------------------------------------------------------------------- % ./echo-server-pthreadrun telnet adonis1 1231 [4509,65536,4] connection from 130.158.86.1:2367 [4509,65537,4] 012 [4509,65536,5] connection from 130.158.86.9:15628 [4509,65538,5] abc [4509,65538,5] def [4509,65538,5] connection closed. [4509,65537,4] 345 [4509,65537,4] connection closed. ^C %
----------------------------------------------------------------------
クライアント側(その2)。---------------------------------------------------------------------- % telnet adonis1 1231Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. 012
012 345
345 ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
---------------------------------------------------------------------- % % telnet adonis1 1231Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. abc
abc def
def ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------