筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2011/2011-06-15/index.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2011/
http://www.coins.tsukuba.ac.jp/~yas/
開設学類 | 情報学群 情報科学類開設 |
科目番号 | GB3 7001 |
授業科目名 | 情報システム特別講義A |
標準履修年次 | 3・4年次 |
単位数 | 1単位 |
担当教員 | 河野 健二(慶應義塾大学 理工学部 准教授) |
授業概要 | 「オペレーティングシステムの仮想化技術」この講義では、仮想機械モニタ (VMM) の仕組みを理解することを目標に VMware Workstation や Xenなどで用いられている実装技術について解説する。1990 年代後半からの技術進展をなぞる形で仮想化技術の肝となる部分について解き明かして行く。 |
日程 | 7月6日(水) 10:00-18:00 |
7月7日(木) 10:00-18:00 | |
教室 | 総合研究棟B棟0112室 |
履修申請 | 平成23年6月24日(金)までTWINSで申請すること。 |
備考 | (世話人教員)システム情報工学研究科 新城 靖 |
HTTP の内容(content)は、バイトの並び。バイト単位のループでもプログラムは書けるが、 遅いので、ある程度の大きさの塊(buffer size単位)で処理するのが普通。 テキストだけ扱うならば、行の並びと仮定しても問題ない。
char *name; name = "Name" printf("Hello, %s\n",name );
char buf[BUFFERSIZE]; snprintf(buf,BUFFERSIZE,"Hello, %s\n",name );
strcpy(), strcat() は論外。
$ telnet www 80
Trying 130.158.86.1...
Connected to www.coins.tsukuba.ac.jp.
Escape character is '^]'.
GET /.. HTTP/1.0
HTTP/1.1 400 Bad Request
Date: Mon, 07 Jun 2010 11:26:36 GMT
Server: Apache
Content-Length: 226
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
</body></html>
Connection closed by foreign host.
$
このうち、1行目、Content-Type:、空行、本文はしっかり返したい。
サーバ側
$ cp ~yas/syspro/ipc/echo-server-nofork-fdopen.c .
$ make echo-server-nofork-fdopen
cc echo-server-nofork-fdopen.c -o echo-server-nofork-fdopen
$ ./echo-server-nofork-fdopen
Usage: ./echo-server-nofork-fdopen portno {ipversion}
$ ./echo-server-nofork-fdopen 1231
run telnet cosmos10(v6) 1231
[9082] connection (fd==4) from 130.158.86.150:58120
[9082] received (fd==4) 5 bytes, [012
]
^C
$
クライアント側(その1)。
$ telnet cosmos10 1231
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
012
012
クライアント側(その2)。
$ telnet cosmos10 1231
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
abc
クライアント(その2)は、クライアント(その1)が終了するまでサービス
を受けられない。
クライアント側(その1)。
$ telnet cosmos10 7
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
012
012
345
345
^]
telnet> ^D
Connection closed.
$
クライアント側(その2)。
$ telnet cosmos10 7
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
abc
abc
123
123
^]
telnet> ^D
Connection closed.
$
1: /* 2: fork-hello.c -- 画面に文字列を表示するプログラム 3: ~yas/syspro/proc/fork-hello.c 4: Start: 2001/05/13 23:19:01 5: */ 6: 7: #include <stdio.h> 8: 9: main() 10: { 11: fork(); 12: fork(); 13: fork(); 14: printf("hello\n"); 15: }実行すると、画面には 8 回 hello と表示される。
$ cp ~yas/syspro/proc/fork-hello.c .
$ make fork-hello
cc fork-hello.c -o fork-hello
$ ./fork-hello
hello
$ hello
hello
hello
hello
hello
hello
hello
$
プロセスは、1回のfork() で、2倍に増える。3回のforkで、2 3 個
に増える。親子関係は複雑。
図? fork()システム・コールによるプロセスのコピー
1: 2: /* 3: echo-server-fork-fdopen.c -- 受け取った文字列をそのまま返すサーバ(fork版) 4: ~yas/syspro/ipc/echo-server-fork-fdopen.c 5: Created on 2004/05/09 19:57:07 6: */ 7: #include <stdio.h> 8: #include <stdlib.h> /* exit() */ 9: #include <sys/types.h> /* socket(), wait4() */ 10: #include <sys/socket.h> /* socket() */ 11: #include <netinet/in.h> /* struct sockaddr_in */ 12: #include <sys/resource.h> /* wait4() */ 13: #include <sys/wait.h> /* wait4() */ 14: #include <netdb.h> /* getnameinfo() */ 15: #include <string.h> /* strlen() */ 16: 17: extern void echo_server( int portno, int ip_version ); 18: extern void delete_zombie(); 19: extern void echo_receive_request_and_send_reply( int com ); 20: extern void print_my_host_port( int portno ); 21: extern void tcp_peeraddr_print( int com ); 22: extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ); 23: extern int tcp_acc_port( int portno, int pf ); 24: extern int fdopen_sock( int sock, FILE **inp, FILE **outp ); 25: 26: main( int argc, char *argv[] ) 27: { 28: int portno, ip_version; 29: if( !(argc == 2 || argc==3) ) { 30: fprintf(stderr,"Usage: %s portno {ipversion}\n",argv[0] ); 31: exit( 1 ); 32: } 33: portno = strtol( argv[1],0,10 ); 34: if( argc == 3 ) 35: ip_version = strtol( argv[2],0,10 ); 36: else 37: ip_version = 4; /* IPv4 by default */ 38: echo_server( portno, ip_version ); 39: } 40:main() は、 echo-server-nofork-fdopen.c と同じである。
41: void 42: echo_server( int portno, int ip_version ) 43: { 44: int acc,com ; 45: pid_t child_pid ; 46: acc = tcp_acc_port( portno, ip_version ); 47: if( acc<0 ) 48: exit( -1 ); 49: print_my_host_port( portno ); 50: while( 1 ) 51: { 52: delete_zombie(); 53: if( (com = accept( acc,0,0 )) < 0 ) 54: { 55: perror("accept"); 56: exit( -1 ); 57: } 58: tcp_peeraddr_print( com ); 59: if( (child_pid=fork()) > 0 ) /* parent */ 60: { 61: close( com ); 62: } 63: else if( child_pid == 0 ) /* child */ 64: { 65: close( acc ); 66: echo_receive_request_and_send_reply( com ); 67: exit( 0 ); 68: } 69: else 70: { 71: perror("fork"); 72: exit( -1 ); 73: } 74: } 75: } 76:
delete_zombie() は、ゾンビ・プロセス(後述)を消去するものである。 (改善の余地がある。) echo サービスとしての処理は、fork() システムコールで分身を作り、子プロ セス側で行う。親プロセスは、すぐにaccept() に戻る。
getpid() は、自分自身の PID (Process ID (identifier)) を返すシステムコー ルである。PID (pid_t) は、32 ビット(システムによっては16ビット)の整数 である。実行結果で printf() の表示に、PID が表示されているが、同じではないことに 注意しなさい。
77: void 78: delete_zombie() 79: { 80: pid_t pid ; 81: while( (pid=wait4(-1,0,WNOHANG,0)) >0 ) 82: { 83: printf("[%d] zombi %d deleted.\n",getpid(),pid ); 84: continue; 85: } 86: }wait4() システムコールを使って、終了した子プロセスをwait してあげてい る。ただし、WNOHANG オプションを付けてあるので、終了した子プロセスがい なければ、待たずに返ってくる。
+
」は、追加された行、
「-
」は、削除された行、
「!
」は、修正された行を意味する。
$ diff -c echo-server-nofork-fdopen.c echo-server-fork-fdopen.c *** echo-server-nofork-fdopen.c 2010-06-07 20:55:04.000000000 +0900 --- echo-server-fork-fdopen.c 2010-06-07 20:55:10.000000000 +0900 *************** *** 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,8 ---- /* ! 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() */ *************** *** 15,20 **** --- 15,21 ---- #include <string.h> /* strlen() */ extern void echo_server( int portno, int ip_version ); + extern void delete_zombie(); extern void echo_receive_request_and_send_reply( int com ); extern void print_my_host_port( int portno ); extern void tcp_peeraddr_print( int com ); *************** *** 41,46 **** --- 42,48 ---- echo_server( int portno, int ip_version ) { int acc,com ; + pid_t child_pid ; acc = tcp_acc_port( portno, ip_version ); if( acc<0 ) exit( -1 ); *************** *** 54,63 **** exit( -1 ); } tcp_peeraddr_print( com ); ! echo_receive_request_and_send_reply( com ); } } #define BUFFERSIZE 1024 void --- 56,90 ---- exit( -1 ); } tcp_peeraddr_print( com ); ! if( (child_pid=fork()) > 0 ) /* parent */ ! { ! close( com ); ! } ! else if( child_pid == 0 ) /* child */ ! { ! close( acc ); ! echo_receive_request_and_send_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 を使うとプログラムが動かないことがある。
$ cp ~yas/syspro/ipc/echo-server-fork-fdopen.c .
$ make echo-server-fork-fdopen
cc echo-server-fork-fdopen.c -o echo-server-fork-fdopen
$ ./echo-server-fork-fdopen
Usage: ./echo-server-fork-fdopen portno {ipversion}
$ ./echo-server-fork-fdopen 1231
run telnet cosmos10(v6) 1231
[9388] connection (fd==4) from 130.158.86.150:58141
[9390] received (fd==4) 5 bytes, [012
]
[9388] connection (fd==4) from 130.158.86.150:58142
[9392] received (fd==4) 5 bytes, [abc
]
[9392] received (fd==4) 5 bytes, [def
]
[9392] connection (fd==4) closed.
[9390] received (fd==4) 5 bytes, [345
]
[9390] connection (fd==4) closed.
^C
$
クライアント側(その1)。
$ telnet cosmos10 1231
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
012
012
345
345
^]
telnet> ^D
Connection closed.
$
クライアント側(その2)。
$ telnet cosmos10 1231
Trying 130.158.86.150...
Connected to cosmos10.coins.tsukuba.ac.jp.
Escape character is '^]'.
abc
abc
def
def
^]
telnet> ^D
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 10120 0.0 0.0 2434760 352 s001 S+ 9:18PM 0:00.00 ./echo-server-fork-fdopen 1231
yas 10520 0.0 0.0 2435040 400 s000 R+ 9:21PM 0:00.00 egrep echo
yas 10193 0.0 0.0 0 0 s001 Z+ 9:18PM 0:00.00 (echo-server-fork)
yas 10156 0.0 0.0 2434760 192 s001 S+ 9:18PM 0:00.00 ./echo-server-fork-fdopen 1231
$
echo-server-fork-fdopen.c では、
delete_zombie() でゾンビを消そうとはしている。しかし、accept() で止まっ
ている状態なので、どこかのクライアントから接続要求が来ない限りは、
delete_zombie() が呼ばれないので、ゾンビとして残っている。
この状態で接続要求が来ると、ゾンビが回収される。
$ ./echo-server-fork-fdopen 1231
run telnet cosmos10(v6) 1231
[10120] connection (fd==4) from 130.158.86.151:62127
[10156] received (fd==4) 5 bytes, [012
]
[10120] connection (fd==4) from 130.158.86.151:62128
[10193] received (fd==4) 5 bytes, [abc
]
[10193] received (fd==4) 5 bytes, [def
]
[10193] connection (fd==4) closed.
[10120] connection (fd==4) from 130.158.86.151:62130
[10120] zombi 10193 deleted.
ゾンビが回収された後の ps コマンドの結果:
$ ps uxw|egrep echo
yas 10655 0.0 0.0 2434760 184 s001 S+ 9:23PM 0:00.00 ./echo-server-fork-fdopen 1231
yas 10156 0.0 0.0 2434760 192 s001 S+ 9:18PM 0:00.00 ./echo-server-fork-fdopen 1231
yas 10120 0.0 0.0 2434760 352 s001 S+ 9:18PM 0:00.00 ./echo-server-fork-fdopen 1231
yas 10713 0.0 0.0 2425524 92 s000 R+ 9:23PM 0:00.00 egrep echo
$
Keep-Alive
に対応しなさい。
ヒント:子プロセスの終了を、ソフトウェア割り込み(signal, SIGCHLD)で知 る方法もある。複数の子プロセスが終了しても、割り込みは1回しか起こらな いことがあることに注意しなさい。
ソフトウェア割込みを使うと、accept() システムコールがエラーで戻って来 るシステムもある。その場合、エラー番号が EINTR なら単純に終了しないで、 再び accept() に向うべきである。
ヒント: IPv4 と IPv6 専用のプロセスやスレッドを作成する。
ヒント: IPv4 の acc と IPv6 の acc を2つ作成し、 両方同時に監視する。
なお同時に扱えるクライアント数は、IPv4 と IPv6 で個別に数えても 統合して数えてもどちらでも良い。たとえば、最大2と設定した場合、 個別に数えて IPv4 で 2, IPv6 で 2、合計 4 としてもよい。