システム・プログラム
電子・情報工学系
新城 靖
<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() システムコール がエラーで戻って来ることがあることにも注意しなさい。