ネットワークプログラミング 第 9 回「...

47
ネネネネネネネネネネネネネ ネ9ネネネネネネネネネネネネネネネネ(1)200 9 ネネネネ Rodney Van Meter

description

2009年秋学期 Rodney Van Meter. ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」. 今期の授業スケジュール(予定). 第1回:イントロダクション 第2回: C 言語の基礎~関数・変数・ Makefile 第3回: C 言語の基礎~コマンドライン引数・構造体・ポインタ 第4回: C 言語の基礎~ポインタと配列・リスト構造 (10/20) ‏ 第5回: File I/O ・ネットワークプログラミング (10/27) 第6回:ネットワークとプログラミング(1) (11/10 ) ‏ - PowerPoint PPT Presentation

Transcript of ネットワークプログラミング 第 9 回「...

Page 1: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

ネットワークプログラミング第 9回「応用ネットワークプログラミング(1)」

200 9年秋学期Rodney Van Meter

Page 2: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

今期の授業スケジュール(予定) 第1回:イントロダクション 第2回: C言語の基礎~関数・変数・Makefile 第3回: C言語の基礎~コマンドライン引数・構造体・ポインタ 第4回: C 言語の基礎~ポインタと配列・リスト構造 (10/20) 第5回: File I/O・ネットワークプログラミング (10/27) 第6回:ネットワークとプログラミング(1) (11/10) 第 7 回:ネットワークとプログラミング(2) (11/17) 第 8回:ネットワークとプログラミング( 3 ) (12/1) 第 9 回:応用ネットワークプログラミング(1) (12/8) 第1 0 回:応用ネットワークプログラミング(2) (12/15) 第1 1 回:ミニプロ 中間発表  (12/22!) 第1 2 回:ミニプロ () 第1 3 回:ミニプロ () 最終発表

Page 3: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

ミニプロについて グループでプログラムを作る OSは問わない

一応、公式サポート環境は ccx 他の環境でも TA/SAは(できるだけ)頑張ります

ネットワークを使うプログラム プログラムの難易度 独創性

Page 4: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

ミニプロの中間発表について Problem (or project) definition (Related work) Key idea How you will evaluate it Schedule

NOT 実装、評価、論文執筆 !

Page 5: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

ミニプロの中間発表について (2)

Team members Division of responsibility How you will work together

What programming environment? What source code control (svn, cvs,

rcs...?) Plan for APIs, functional definition Testing plan

Page 6: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

ミニプロの提出 提出は、ソースコードならびにレポート

提出ソースコードにはコメントを付加 日本語 or 英語

レポートには以下の項目を含める 何を作ったのか 使用方法 面白さ 実行環境

Page 7: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

ミニプロのプレゼン 中間発表

再来週 何を作るかをプレゼンする

A4 2-3枚程度のレジュメを作成する

最終発表 授業最終回を予定 できあがりをプレゼンする 評価

Page 8: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

ミニプロのメンバー 申請したい人は 12/8(火 )までにメール

最大人数 : 3-4人

申請が無い人はこちらで振り分けます

Page 9: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

今日のお題 (forkします ) 講義

前回のおさらい TCP-server

listen() accept()

forkサーバの挙動プロセス練習 1 : forkに慣れよう実習:平行サーバを作成

Page 10: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

TCP 通信の流れ (server)socket()

bind()

listen()

accept()

socket()

connect()

read()

close()

TCP client

TCP Server

write()read()

write()

read()

close()

data

data

establish

end

Page 11: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork

Page 12: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

よくあるサーバー

サーバー

クライアント

クライアント

クライアントクライアント

クライアント

クライアント

集中!

複数のクライアントから同時にアクセスを受ける

Page 13: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

( ‘ ’前回の 使い方例 )の問題点 一つのクライアントに対応している間は、他のクライアントに対応できない! 複数のクライアントが一度にアクセスした場合を考慮しなければいけない

どこがネックか? accept()を多重化して実行できると良い? 実際の処理を多重化して実行できると良い?

Page 14: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

サーバの気持ちになってみよう たくさんのソケットを扱ったり,めんどくさいことはしたくない いちいちソケットを検査するのが面倒 どれにどれだけ時間がかけられるんだ?

沢山のクライアントが接続してきたら,大変

Page 15: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

自分の分身を作ればいいんだ!

t

親プロセス

子プロセス二号

子プロセス一号

仕事は全部,分身にまかせよう 親の仕事は,コネクションがきたときに分身を作ることだけ

Page 16: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

平行処理 (concurrent processing) 素早い応答 多くの要求を処理

C1

C2

C3 C4

server

connect()read()write()

listen()accept()fork()

recv()send()C1:process

C2

C3

C4

Page 17: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

分身のための関数 forkシステムコール

int fork(); 自分とまったく同じ複製プロセスを作るシステムコール

自分がコピーかどうかわからなくならない? 0が帰ってきたら自分は子プロセス 正の整数が帰ってきたら自分は fork()を呼び出した親プロセス

-1は失敗

Page 18: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

プロセスを見る Linux系 (ccx00)

% ps aux Solaris 又は Linux

% ps -efj BSD系又は Linux

% ps auxj

USER PID PPID PGID …………… COMMANDnobody 123 89 89 …………… httpd

Page 19: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

プログラムの実行とプロセス

main(int argc, char *argv){ struct sockaddr_in me, peer; ……. if(pid = fork()) == 0) {

child process. } else if(pid > 0) {

parenet process. } else {

error process. }}

プログラム

OS

実行

プロセス

生成

プログラム

fork()

プロセス

生成

}

プログラム

親プロセス 子プロセス

コピー・・・・・・・・・・・・・・・・・・・・・・・・・・・

・・・・・・・・・・・・・・・・・・・・・・・・・・・

Page 20: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

親と子の区別fork()の返り値

int childpid;childpid = fork();

親側 子側

if(childpid > 0){ 親の処理 ;}

else if(childpid == 0){ 子の処理}

Page 21: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

よくあるサーバでの処理の流れ 時間のかかるサービス

(telnet, ssh, ftpなど )を提供するサーバでよくある処理

接続要求があったら, acceptして,とりあえず fork(); 子プロセスだったら、サービスの提供開始 親プロセスだったら、接続要求待ち

自分でたくさんのソケットの面倒を見なくてよい

Page 22: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork()を用いたマルチクライアントサーバの仕組み

クライアント 1クライアント 1

クライアント 2クライアント 2

クライアント 3クライアント 3

サーバサーバ

listen()してクライアントからのリクエストを待っている状態

ファイルディスクリプタ

Page 23: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork()を用いたマルチクライアントサーバの仕組み

クライアント 1クライアント 1

クライアント 2クライアント 2

クライアント 3クライアント 3

サーバサーバ

クライアントからリクエストが飛んで来た (accept()する直前 )

リクエスト

ファイルディスクリプタ

Page 24: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork()を用いたマルチクライアントサーバの仕組み

クライアント 1クライアント 1

クライアント 2クライアント 2

クライアント 3クライアント 3

サーバサーバ

サーバ側で accept()し、子プロセスを生成

リクエストaccept()

ファイルディスクリプタ

サーバ (子 )サーバ )子 (

fork()

Page 25: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork()を用いたマルチクライアントサーバの仕組み

クライアント 1クライアント 1

クライアント 2クライアント 2

クライアント 3クライアント 3

サーバ (親 )サーバ )親 (

親プロセスは accept()が作成したファイルディスクリプタを閉じ、子プロセスは listen()しているファイルディスクリプタを閉じる

ファイルディスクリプタ

サーバ (子 )サーバ )子 (

Page 26: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork()を用いたマルチクライアントサーバの仕組み

クライアント 1クライアント 1

クライアント 2クライアント 2

クライアント 3クライアント 3

子プロセスがクライアント 1からのリクエストを処理親プロセスは他のクライアントからの接続要求を待ちつづける

ファイルディスクリプタ

サーバ (親 )サーバ )親 (

サーバ (子 )サーバ )子 (

Page 27: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork()を用いたマルチクライアントサーバの仕組み

クライアント 1クライアント 1

クライアント 2クライアント 2

クライアント 3クライアント 3 新たなクライアントからのリクエストを受け付けると、再びfork()し子プロセスを作成する

ファイルディスクリプタ

サーバ (親 )サーバ )親 (

サーバ (子 )サーバ )子 (

サーバ (子 )サーバ )子 (

fork()

accept()

Page 28: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

練習 1 : forkに触れてみよう (次ページに解説あり )

#include <sys/types.h>#include <stdio.h>#include <unistd.h>int main(int argc, char *argv[]){ int pid; int number = 5; char buf[1];

printf("original process id: %d\n", getpid()); printf("original parent process id: %d\n", getppid());

if( (pid = fork()) == 0){ /* child process */ printf("this is child process\n"); number += 10; printf("number: %d\n", number); printf("fork() return value: %d\n", pid); printf("child process id: %d\n", getpid()); printf("child parent process id: %d\n", getppid()); } else if(pid > 0){ /* parent process */ printf("this is parent process\n"); number += 20; printf("number: %d\n", number); printf("fork() return value: %d\n", pid); printf("parent process id: %d\n", getpid()); printf("parent parent process id: %d\n", getppid()); } else { perror("fork()"); } printf("number: %d\n", number); exit(0);}

Page 29: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

練習 1:覚えた方がお得かも int getpid()

プロセス idを取得 int getppid()

親プロセス idを取得

Page 30: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

練習 1 ①:重要な点

forkの基本構文 int pid;

if( (pid = fork()) == 0){ /* child process */} else if(pid > 0){ /* parent process */} else { perror("fork()");}

Page 31: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

練習 1 ②:重要な点 変数がコピーされる

int numberの中身を考えてみよう 別のメモリ領域が確保される

original process id: 21146original parent process id: 20587

this is parent processnumber: 25fork() return value: 21147parent process id: 21146parent parent process id: 20587number: 25

this is child processnumber: 15fork() return value: 0child process id: 21147child parent process id: 21146number: 15

Page 32: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

fork()を利用した並行サーバの注意 点 (親プロセスの義務)

子プロセスが終了すると、「どのような死に様だったか」が親プロセスに伝えられる システムが親プロセスに通知 シグナルを利用

非同期な事象をプログラムで扱うための方法 親プロセスは子プロセスの死に様を見届けなければならない

親プロセスが子プロセスの終了状態を受け取らなくては,子プロセスは成仏できずにゾンビプロセスに変わる psコマンドで確認すると, a.out <defunct>と表示される

子プロセスが死んで,終了状態を受け取らずに親プロセスが死ぬと,ゾンビプロセスがいつまでも残る

子プロセスが終了する前に親プロセスが死ぬと? プロセス ID1の initが養子として引き取ってくれる

Page 33: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

終了状態の受け渡し

子プロセス 子プロセス

親プロセス

Page 34: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

終了状態の受け渡し

子プロセス 子プロセス

親プロセス

終了(正常 /異常 )

Page 35: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

終了状態の受け渡し

ゾンビプロセス 子プロセス

親プロセス

終了(正常 /異常 )

Page 36: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

終了状態の受け渡し

ゾンビプロセス 子プロセス

親プロセス

終了(正常 /異常 )

終了した子プロセスから状態を取得する

Page 37: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

終了状態の受け渡し

ゾンビプロセス 子プロセス

親プロセス

シグナルSIGCHLD

正確にはプロセスを管理している OSから通知される

Page 38: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

終了状態の受け渡し

ゾンビプロセス 子プロセス

親プロセス

消滅

Page 39: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

親プロセスの義務 つまり、子プロセスが終了したというシグナルを受け取ったら「南無阿弥陀仏」と唱えなければいけない 子プロセスの終了状態 (ステータス )を受け取る

呪文: wait() システムコール 子プロセスの状態を受け取り、変数へ代入 子プロセスの終了状態から、適切な処理を進められる

注意: シグナルのキューイング 複数のシグナルを同時に受信しても「1つ」しか通知されない

複数の子プロセスが同時に死んだ場合の処理を工夫

Page 40: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

時間的な処理の流れ

何かの処理シグナルハンドラ

子プロセスの終了(シグナル発生 )

処理終了

自動的に呼ばれる

中断した所から実行を再開

Page 41: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

waitシステムコール群 子プロセスの状態 (ステータス )を取得するためのシステムコール

pid_t wait(int *status); pid_t waitpid(pid_t wpid, int *status,

int option); 子プロセスの pidが戻り値 失敗 : -1

Page 42: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

wait()とwaitpid()の違い waitは子プロセスが終了する(ほんとは状態が変わる)までブロックする 親プロセスは別の処理を行えない。

waitpid() (see also wait4() on some OSes) ブロックしないようにするオプションがある。

WNOHANG 戻り値 0は、もう状態の変化したプロセスがない場合

pidを指定できる -1 を指定すると最初に状態の変化した子プロセス

0を指定すると、同一プロセスグループ idを持つ子プロセス

Page 43: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

サンプルコード

void sig_child(int signo){

int pid, status;while((pid = waitpid(-1, &status, WNOHANG)) > 0) {

printf("PID: %d, terminated\n", pid);}

}

int main(int argc, char *argv[]){

….signal(SIGCHLD, sig_child);….

}

Page 44: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

実習 以前つくった TCP-echo-severを forkを用いて並行処理可能にする

前回とに違い Server:

forkする “クライアントから bye”を受け取ると通信を終了する

bye以外は全て clientにエコーする waitpidする (子プロセスの終了を受け取る )

複数クライアントからサーバに接続し, psコマンドを用いて forkが出来ていることを確認すること

Page 45: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

知っておきたいシグナル SIGINT

Ctrl-Cにバインドされていて、プロセスを終了するためによく使われる

SIGTERM これもプロセスを終了するためによく使われる。 killコマンドが

defaultで送るシグナル SIGTSTP

Ctrl-Zにバインドされていて、プロセスをサスペンドするためによく使われる。

SIGCHLD fork()で作成した子プロセスが exit()したことを通知するシグナル。

これを受けて wait() などの後処理を走らせる SIGALRM

alarm()によって指定された秒数後に送られるシグナル。タイマーの実装でよく使う

Page 46: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

シグナルをする時 kill(pid_t pid, int sig)というシステムコール

pid == どちのプロセスに送る sig == シグナルナンバー

Page 47: ネットワークプログラミング 第 9 回「 応用ネットワークプログラミング(1)」

シグナルハンドラの指定:signal() void (*signal(int signo, void (*func)(int)))

(int); 指定したシグナル発生時に呼び出される関数(シグナルハンドラ)へのポインタを指定する

以前に設定されていたシグナルハンドラを指すポインタが戻り値

Signoは捕捉するシグナル funcはシグナルハンドラへのポインタ

第 2引数の funcは引数として整数をひとつとり、戻り値を返さない関数へのポインタ