システム・プログラムI 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1998/1998-06-09
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
---------------------------------------------------------------------- % egrep day /etc/servicesdaytime 13/tcp daytime 13/udp % telnet adonis1 13
Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. Mon Jun 8 19:37:03 1998 Connection closed by foreign host. %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: 2: /* 3: rdaytime-client.c -- 遠隔のホストの日付を調べる 4: ~yas/syspro1/ipc/rdaytime-client.c 5: $Header: /home/lab2/OS/yas/syspro1-1998/ipc/RCS/rdaytime-client.c,v 1.6 1998/06/08 12:13:46 yas Exp $ 6: Start: 1995/09/08 21:04:33 7: */ 8: #include <stdio.h> 9: #include <sys/types.h> /* socket() */ 10: #include <sys/socket.h> /* socket() */ 11: #include <netinet/in.h> /* struct sockaddr_in */ 12: #include <netdb.h> /* gethostbyname() */ 13: 14: extern int tcp_connect( char *hostname, int portno ); 15: extern void rdaytime_client( char *hostname, int portno ); 16: 17: main( int argc, char *argv[] ) 18: { 19: if( argc != 3 ) 20: { 21: fprintf( stdout,"Usage: %s host port\n",argv[0] ); 22: exit( -1 ); 23: } 24: rdaytime_client( argv[1],atoi(argv[2]) ); 25: } 26: 27: void rdaytime_client( char *hostname, int portno ) 28: { 29: int s ; 30: int rcount ; 31: char buf[BUFSIZ]; 32: 33: s = tcp_connect( hostname, portno ); 34: if( s<0 ) 35: exit( -1 ); 36: while( (rcount=read(s,buf,BUFSIZ)) > 0 ) 37: { 38: if( write(1,buf,rcount) != rcount ) 39: { 40: perror("write"); 41: exit( 1 ); 42: } 43: } 44: close( s ); 45: } 46: 47: int tcp_connect( char *hostname, int portno ) 48: { 49: struct hostent *hostent ; 50: struct sockaddr_in addr ; 51: int addr_len ; 52: int s ; 53: 54: addr.sin_family = AF_INET ; 55: if( (hostent = gethostbyname( hostname )) == NULL ) 56: { 57: fprintf(stderr,"unknown host %s\n",hostname ); 58: return( -1 ); 59: } 60: bcopy( hostent->h_addr, &addr.sin_addr, hostent->h_length ); 61: addr.sin_port = htons( portno ); 62: if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) 63: { 64: perror("socket"); 65: return( -1 ); 66: } 67: if( connect(s, &addr, sizeof(addr)) < 0 ) 68: { 69: perror( hostname ); 70: close( s ); 71: return( -1 ); 72: } 73: return( s ); 74: } ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ./rdaytime-client adonis1 13Mon Jun 8 19:40:16 1998 %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: 2: /* 3: rdaytime-server.c -- 日付を返すサーバ 4: ~yas/syspro1/ipc/rdaytime-server.c 5: $Header: /home/lab2/OS/yas/syspro1-1998/ipc/RCS/rdaytime-server.c,v 1.5 1998/06/08 11:36:59 yas Exp $ 6: Start: 1995/09/08 21:04:33 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 <time.h> /* time() */ 13: 14: 15: extern rdaytime_server( int portno ); 16: extern rdaytime_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: rdaytime_server( portno ); 35: } 36: 37: rdaytime_server( int portno ) 38: { 39: int acc,com ; 40: int rcount ; 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: rdaytime_reply( com ); 54: } 55: } 56: 57: rdaytime_reply( int com ) 58: { 59: time_t t ; 60: char *p ; 61: int len ; 62: 63: t = time( 0 ); 64: p = ctime( &t ); /* gettimeofday() if BSD */ 65: len = strlen( p ); 66: if( writen( com, p, len ) != len ) 67: { 68: perror("write"); 69: exit( -1 ); 70: } 71: close( com ); 72: } 73: 74: void print_host_port( int portno ) 75: { 76: char hostname[100] ; 77: gethostname( hostname,sizeof(hostname) ); 78: hostname[99] = 0 ; 79: printf("run telnet %s %d \n",hostname, portno ); 80: } 81: 82: void tcp_peeraddr_print( int com ) 83: { 84: struct sockaddr_in addr ; 85: int addr_len ; 86: union { 87: int i ; 88: unsigned char byte[4] ; 89: } x ; 90: addr_len = sizeof( addr ); 91: if( getpeername( com, &addr, &addr_len )<0 ) 92: { 93: perror("print_peeraddr"); 94: } 95: x.i = addr.sin_addr.s_addr ; 96: printf("[%d,%d] connection from %d.%d.%d.%d:%d\n",getpid(),com, 97: x.byte[0],x.byte[1],x.byte[2],x.byte[3], 98: ntohs( addr.sin_port )); 99: } 100: 101: tcp_acc_port( int portno ) 102: { 103: struct hostent *hostent ; 104: struct sockaddr_in addr ; 105: int addr_len ; 106: int s ; 107: 108: if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) 109: { 110: perror("socket"); 111: return( -1 ); 112: } 113: 114: addr.sin_family = AF_INET ; 115: addr.sin_addr.s_addr = INADDR_ANY ; 116: addr.sin_port = htons( portno ); 117: 118: if( bind(s,&addr,sizeof(addr)) < 0 ) 119: { 120: perror( "bind" ); 121: fprintf(stderr,"port number %d is already used. wait a moment or kill another program.\n", portno ); 122: return( -1 ); 123: } 124: listen( s, 5 ); 125: return( s ); 126: } 127: 128: /* See: UNIX Network Programming, SS.6.6, Utility routines */ 129: 130: int writen( int fd, char *buf, int nbytes ) 131: { 132: register int nleft, nwritten ; 133: 134: nleft = nbytes ; 135: while( nleft > 0 ) 136: { 137: nwritten = write( fd, buf, nleft ); 138: if( nwritten <= 0 ) 139: return( nwritten ); 140: nleft -= nwritten ; 141: buf += nwritten ; 142: } 143: return( nbytes - nleft ); 144: } ----------------------------------------------------------------------実行例。サーバ側。サーバは、終了しないので、最後に、^C か Del を押して、割り込みを掛けて終了させる。
実行例。クライアント側。telnet か rdaytime-client を使う。---------------------------------------------------------------------- % ./rdaytime-serverrun telnet adonis1 1231 [13405,4] connection from 130.158.86.1:11108 [13405,4] connection from 130.158.86.1:11109 [13405,4] connection from 130.158.86.1:11110 [13405,4] connection from 130.158.86.1:11111 ^C %
----------------------------------------------------------------------
---------------------------------------------------------------------- % telnet adonis1 1231Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. Mon Jun 8 20:07:10 1998 Connection closed by foreign host. % telnet adonis1 1231
Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. Mon Jun 8 20:07:16 1998 Connection closed by foreign host. % ./rdaytime-client adonis1 1231
Mon Jun 8 20:07:19 1998 % ./rdaytime-client adonis1 1231
Mon Jun 8 20:07:20 1998 %
----------------------------------------------------------------------
---------------------------------------------------------------------- 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-selectrun telnet adonis1 1231 [1701,4] connection from 130.158.86.1:20822 [1701,4] 012 [1701,5] connection from 130.158.86.11:1479 [1701,5] abc [1701,5] def [1701,5] connection closed. [1701,4] 345 [1701,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. %
----------------------------------------------------------------------
ps -l コマンドで見ると、ゾンビの状態(S) は、Z と表示される。
CMD は、defunct
(故人となった、消滅した)と表示される。
% ps -lu $USERF S UID PID PPID C PRI NI P SZ:RSS WCHAN TTY TIME CMD b0 S 1231 13105 13104 0 39 20 * 618:228 802737f0 pts/2 0:02 tcsh 90 Z 1231 13614 13609 0 0 - - - - - - 0:00 <defunct> b0 S 1231 13609 13105 0 60 20 * 359:44 8079a958 pts/2 0:00 echo-serv b0 S 1231 13076 13075 1 39 20 * 614:221 802737f0 pts/1 0:01 tcsh b0 R 1231 13623 13076 3 61 20 0 397:79 - pts/1 0:00 ps 90 Z 1231 13617 13609 0 0 - - - - - - 0:00 <defunct> b0 T 1231 13142 13076 0 60 20 * 2229:1052 - pts/1 0:15 emacs %
![]()
ヒント:accept() で止まる前に、wait() する方法がある。ただし、単純に wait() すると、fork() の効果がまったくなくなる。子プロセスが終了したと きだけ、wait() したい。それには、waitpid() や wait3(), wait4() で、あ るオプションを使うとよい。
ヒント:子プロセスの終了を、 ソフトウェア割り込み ( SIGCHLD) で知る方法もある。複数の子プロセスが終了しても、割り込みは1回しか起こ らないことがあることに注意しなさい。
ソフトウェア割込みについては、 来週 話します。
pipe() の代りに、TCP/IP のストリームを使って、異なるホスト上のプロセス を接続して、フィルタの処理を行わせてみなさい。
ヒント:例題 pipe-rw.cを tcp_connect() やtcp_acc_port() を使って書き直すとよい。 ただし、別のホストで。fork() は、しなくてもよいはず。
TCP/IP なら本来双方向で使える。しかし、この練習問題だと単方向だけを使 う。こうすると、パイプと同じような使い方ができる。
練習問題51 で、3つ以上のプロセスを接続できるようにしなさい。
ヒント: ★練習問題40 3個のプロセスをパイプで結ぶ相当。 プログラムは、1つではなく、最初用、中間用、最後用の3つを作る方法があ る。中間用のものは、うまく作ると、何個でもはさみ込めるようにできるはず。 fork() は、しなくてもよいはず。
ヒント:dup(), dup2(), close() などで、標準入出力を切り替えて、 execve() などで、プログラムを実行する。
たとえば、シェルに次のように打ち込むことを考える。
% command1 | command2 | command3この時、シェルは、fork() しながら2つのパイプで3つのプロセスを結び、 execve() など command1, command2, command3 を実行する。この問題では、 パイプではなくTCP/IP 結ぶ。そして、close(), dup(), close() して、 execve() でcommand1, command2, command3 を実行する。fork() は、しなく てもよいはず。
上の tcp_peeraddr_print() は、IP アドレスを数字で表示していた。 これを、ホスト名で表示するようにしなさい。 それには、gethostbyaddr()を利用するとよい。
普通は、ホスト名からIPアドレスを調べる手続き gethostbyname() が使われ る。gethostbyaddr() は、その逆を行う手続きである。