システム・プログラム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() は、その逆を行う手続きである。