システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro-2000/2000-05-15
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
---------------------------------------------------------------------- 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() で、WNOHANG オプションを使い、終了したプロセスが存在 する間、それらを全て待ち消去すればよい。
ヒント:子プロセスの終了を、 ソフトウェア割り込み (SIGCHLD)で知る方法もある。複数の子プロセスが終了しても、割り込みは1 回しか起こらないことがあることに注意しなさい。accept() システムコール がエラーで戻って来ることがあることにも注意しなさい。