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
%