筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.coins.tsukuba.ac.jp/~syspro/2008/No7_files/echo-server-fork.html
あるいは、次のページから手繰っていくこともできます。
	http://www.coins.tsukuba.ac.jp/~syspro/2008/
	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 
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
% 
クライアント側(その1)。
% telnet azalea20.coins.tsukuba.ac.jp 1231  
Trying 130.158.86.40...
Connected to azalea20.coins.tsukuba.ac.jp.
Escape character is '^]'.
012
012
345
345
^]
telnet> quit
Connection closed.
% 
クライアント側(その2)。
% telnet azalea20.coins.tsukuba.ac.jp 1231  
Trying 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 echo 
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.c では、
delete_zombie() でゾンビを消そうとはしている。しかし、accept() で止まっ
ている状態なので、どこかのクライアントから接続要求が来ない限りは、
delete_zombie() が呼ばれないので、ゾンビとして残っている。
この状態で接続要求が来ると、ゾンビが回収される。
% ./echo-server-fork-fdopen 1231 
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 コマンドの結果:
% ps uxw | egrep echo 
yas  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
%