筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2007/No7_files/echo-server-fork.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2007/
http://www.coins.tsukuba.ac.jp/~yas/
1: /* 2: echo-server-fork-fdopen.c -- 受け取った文字列をそのまま返すサーバ(fork版) 3: ~yas/syspro/ipc/echo-server-fork-fdopen.c 4: Created on 2004/05/09 19:57:07 5: */ 6: #include <stdio.h> 7: #include <stdlib.h> /* exit() */ 8: #include <sys/types.h> /* socket(), wait4() */ 9: #include <sys/socket.h> /* socket() */ 10: #include <netinet/in.h> /* struct sockaddr_in */ 11: #include <sys/resource.h> /* wait4() */ 12: #include <sys/wait.h> /* wait4() */ 13: #include <netdb.h> /* getnameinfo() */ 14: #include <string.h> /* strlen() */ 15: 16: extern void echo_server( int portno ); 17: extern void echo_reply( int com ); 18: extern void delete_zombie(void); 19: extern void print_my_host_port( int portno ); 20: extern void tcp_peeraddr_print( int com ); 21: extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ); 22: extern tcp_acc_port( int portno ); 23: extern int fdopen_sock( int sock, FILE **inp, FILE **outp ); 24: 25: main( int argc, char *argv[] ) 26: { 27: int portno ; 28: if( argc >= 3 ) 29: { 30: fprintf( stdout,"Usage: %s [portno] \n",argv[0] ); 31: exit( -1 ); 32: } 33: if( argc == 2 ) 34: portno = strtol( argv[1],0,10 ); 35: else 36: portno = getuid(); 37: echo_server( portno ); 38: } 39:main() は、 echo-server-nofork-fdopen.c と同じである。
40: void 41: echo_server( int portno ) 42: { 43: int acc,com ; 44: pid_t child_pid ; 45: acc = tcp_acc_port( portno ); 46: if( acc<0 ) 47: exit( -1 ); 48: print_my_host_port( portno ); 49: while( 1 ) 50: { 51: delete_zombie(); 52: if( (com = accept( acc,0,0 )) < 0 ) 53: { 54: perror("accept"); 55: exit( -1 ); 56: } 57: tcp_peeraddr_print( com ); 58: if( (child_pid=fork()) > 0 ) /* parent */ 59: { 60: close( com ); 61: } 62: else if( child_pid == 0 ) /* child */ 63: { 64: close( acc ); 65: echo_reply( com ); 66: exit( 0 ); 67: } 68: else 69: { 70: perror("fork"); 71: exit( -1 ); 72: } 73: } 74: } 75:
delete_zombie() は、ゾンビ・プロセス(後述)を消去するものである。 (改善の余地がある。) echo サービスとしての処理は、fork() システムコールで分身を作り、子プロ セス側で行う。親プロセスは、すぐにaccept() に戻る。
getpid() は、自分自身の PID (Process ID (identifier)) を返すシステムコー ルである。PID (pid_t) は、16 ビット(システムによっては32ビット)の整数 である。実行結果で printf() の表示に、PID が表示されているが、同じではないことに 注意しなさい。
76: void 77: delete_zombie() 78: { 79: pid_t pid ; 80: while( (pid=wait4(-1,0,WNOHANG,0)) >0 ) 81: { 82: printf("[%d] zombi %d deleted.\n",getpid(),pid ); 83: continue; 84: } 85: }wait4() システムコールを使って、終了した子プロセスをwait してあげてい る。ただし、WNOHANG オプションを付けてあるので、終了した子プロセスがい なければ、待たずに返ってくる。
他の関数は、 echo-server-nofork-fdopen.c と同じである。
% diff -c echo-server-nofork-fdopen.c echo-server-fork-fdopen.c *** echo-server-nofork-fdopen.c Sun May 21 20:12:41 2006 --- echo-server-fork-fdopen.c Sun May 21 20:55:59 2006 *************** *** 1,8 **** - /* ! echo-server-nofork-fdopen.c -- (fork) ! ~yas/syspro/ipc/echo-server-nofork-fdopen.c ! Created on 2004/05/09 19:08:47 */ #include <stdio.h> #include <stdlib.h> /* exit() */ --- 1,7 ---- /* ! echo-server-fork-fdopen.c -- (fork) ! ~yas/syspro/ipc/echo-server-fork-fdopen.c ! Created on 2004/05/09 19:57:07 */ #include <stdio.h> #include <stdlib.h> /* exit() */ *************** *** 16,21 **** --- 15,21 ---- extern void echo_server( int portno ); extern void echo_reply( int com ); + extern void delete_zombie(void); extern void print_my_host_port( int portno ); extern void tcp_peeraddr_print( int com ); extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ); *************** *** 41,62 **** echo_server( int portno ) { int acc,com ; acc = tcp_acc_port( portno ); if( acc<0 ) exit( -1 ); print_my_host_port( portno ); while( 1 ) { if( (com = accept( acc,0,0 )) < 0 ) { perror("accept"); exit( -1 ); } tcp_peeraddr_print( com ); ! echo_reply( com ); } } #define BUFFERSIZE 1024 void --- 41,89 ---- echo_server( int portno ) { int acc,com ; + pid_t child_pid ; acc = tcp_acc_port( portno ); if( acc<0 ) exit( -1 ); print_my_host_port( portno ); while( 1 ) { + delete_zombie(); if( (com = accept( acc,0,0 )) < 0 ) { perror("accept"); exit( -1 ); } tcp_peeraddr_print( com ); ! if( (child_pid=fork()) > 0 ) /* parent */ ! { ! close( com ); ! } ! else if( child_pid == 0 ) /* child */ ! { ! close( acc ); ! echo_reply( com ); ! exit( 0 ); ! } ! else ! { ! perror("fork"); ! exit( -1 ); ! } } } + void + delete_zombie() + { + pid_t pid ; + while( (pid=wait4(-1,0,WNOHANG,0)) >0 ) + { + printf("[%d] zombi %d deleted.\n",getpid(),pid ); + continue; + } + } + #define BUFFERSIZE 1024 void %
実行例。
サーバ側。 サーバは、終了しないので、最後に、^C を押して、割り込みを掛け て終了させる。
注意:全員がポート番号 1231 を使うとプログラムが動かないことがある。
% ./echo-server-fork-fdopen 1231クライアント側(その1)。run telnet azalea20.coins.tsukuba.ac.jp 1231 [17559] connection (fd==4) from 130.158.86.40:55958 [17561] received (fd==4) 5 bytes, [012 ] [17559] connection (fd==4) from 130.158.86.50:52792 [17562] received (fd==4) 5 bytes, [abc ] [17562] received (fd==4) 5 bytes, [def ] [17562] connection (fd==4) closed. [17561] received (fd==4) 5 bytes, [345 ] [17561] connection (fd==4) closed. ^C %
![]()
% telnet azalea20.coins.tsukuba.ac.jp 1231クライアント側(その2)。Trying 130.158.86.40... Connected to azalea20.coins.tsukuba.ac.jp. Escape character is '^]'. 012
012 345
345 ^] telnet> quit
Connection closed. %
![]()
% telnet azalea20.coins.tsukuba.ac.jp 1231Trying 130.158.86.40... Connected to azalea20.coins.tsukuba.ac.jp. Escape character is '^]'. abc
abc def
def ^] telnet> quit
Connection closed. %
![]()
図1 サーバが accept() で接続要求を待っている
図2 サーバが accept()している時にクライアントが connect()した所
図3 accept() の結果、通信用ポート com が作られる
図4 fork() して、親子に別れる。
図5 親は、com を close()、子は acc を close() する
図6 親は、再び accept() で待ち、子は、特定のクライアントに対して read()/write() する。
図7 サーバが別のクライアントから接続要求を受け付ける
図8 accept() の結果、通信用ポート com が作られる
図9 fork() して、親子に別れる。
図10 親は、com を close()、子は acc を close() する
図11 親は、再び accept() で待ち、子は、特定のクライアントに対して read()/write() する。
ps コマンドで見ると、ゾンビの状態(STAT) は、Z と表示される。
% ps uxw | egrep echoecho-server-fork-fdopen.c では、 delete_zombie() でゾンビを消そうとはしている。しかし、accept() で止まっ ている状態なので、どこかのクライアントから接続要求が来ない限りは、 delete_zombie() が呼ばれないので、ゾンビとして残っている。yas 17618 0.0 -0.0 0 0 p4 Z+ 1Jan70 0:00.00 (echo-server-fork) yas 17616 0.0 -0.0 36640 348 p4 S+ 9:19PM 0:00.01 ./echo-server-fork-fdopen 1231 yas 17619 0.0 -0.0 0 0 p4 Z+ 1Jan70 0:00.00 (echo-server-fork) yas 17626 0.0 -0.0 8776 8 p5 R+ 9:21PM 0:00.00 egrep echo %
![]()
この状態で接続要求が来ると、ゾンビが回収される。
% ./echo-server-fork-fdopen 1231ゾンビが回収された後の ps コマンドの結果:run telnet adonis9.coins.tsukuba.ac.jp 1231 [6907] connection (fd==4) from 130.158.86.28:47332 [6908] read(4,,) 5 bytes, [012 ] [6907] connection (fd==4) from 130.158.86.29:42126 [6910] read(4,,) 5 bytes, [abc ] [6910] read(4,,) 5 bytes, [def ] [6910] connection (fd==4) closed. [6908] read(4,,) 5 bytes, [345 ] [6908] connection (fd==4) closed. [6907] connection (fd==4) from 127.0.0.1:42127 [6907] zombi 6910 deleted. [6907] zombi 6908 deleted.
% ps uxw | egrep echoyas 17631 0.0 -0.0 8776 8 p5 R+ 9:23PM 0:00.00 egrep echo yas 17616 0.0 -0.0 36640 360 p4 S+ 9:19PM 0:00.01 ./echo-server-fork-fdopen 1231 yas 17629 0.0 -0.0 36800 156 p4 S+ 9:23PM 0:00.00 ./echo-server-fork-fdopen 1231 %
![]()