ネットワーク・プログラミング パイプライン通信とシグナル
全体の位置づけ オペレーションシステム(Linux) ソケットプログラミング プロセス カーネルとシステムコール プロセス間通信 スレッド クライアントーサーバ 非同期I/Oとマルチタスク
1.1 パイプラインとシグナル プロセス A プロセス B パイプバッファ カーネル write用 記述子 read用 記述子 fd[0] 1.1 パイプラインとシグナル write用 記述子 プロセスA pipe(fd); プロセスB pipe(fd); プロセス A プロセス B read用 記述子 fd[0] fd[1] fd[0] fd[1] プロセス間通信 パイプバッファ パイプライン シグナル プロセスA signal(SIG,stop); void stop() { … } プロセスB Ctlr-S 共有メモリ セマフォ ソケット カーネル シグナル
1.2 標準入出力とパイプライン 標準入力: キーボード 標準出力: ディスプレイ < : 標準入力をファイルへ 1.2 標準入出力とパイプライン 標準入力: キーボード 標準出力: ディスプレイ < : 標準入力をファイルへ > : 標準出力をファイルへ |: パイプライン (前のコマンドの標準出力を後のコマンドの標準入力にする) [oida@rpc261 soft2]$ tr o i ooki iiki oida iida okajima ikajima [oida@rpc261 soft2]$ cat > input [oida@rpc261 soft2]$ tr o i < input [oida@rpc261 soft2]$ tr o i < input > output [oida@rpc261 soft2]$ cat output [oida@rpc261 soft2]$ tr o i < input | sort > output [oida@rpc261 soft2]$ 入力 出力 ctrl-D ctrl-D パイプライン
2.1 パイプライン通信 親プロセス 子プロセス パイプ バッファ read用 write用 fd[0] fd[0] fd[1] fd[0] 2.1 パイプライン通信 親プロセス パイプラインを生成 標準入力から数字を入力 子プロセスchildを生成 パイプラインに数字を書込む 子プロセス終了後パイプラインを読む 読み出した数値を出力 子プロセス 引数からファイルディスクリプタを得る パイプラインから数字を読む 数字を2乗する 2乗した結果をパイプラインに書き込む oida@rpc261 soft2]$ gcc -o parent parent.c [oida@rpc261 soft2]$ gcc -o child child.c [oida@rpc261 soft2]$ ./parent 512 512 x 512 = 262144 [oida@rpc261 soft2]$ pipe パイプを作る インクルードファイル #include <unistd.h> 書式 int pipe(int filedes[2]); 戻値 成功時 0 失敗時 -1 親プロセス pipe(fd); fork(); 子プロセス execl(“child”); プロセス pipe(fd); パイプ バッファ fd[0] 親のfd[1]のコピー fd[0] fd[1] fd[0] fd[1] fd[0] = 3 fd[1] = 4 fd[1] 512 512 x 512 read用 write用
2.2 パイプライン通信 整数を文字列へ parent.c int main() { 2.2 パイプライン通信 parent.c int main() { char line[32], read_fd[3], write_fd[3]; int number, result, fd[2], st; if (pipe(fd)<0) { perror(“pipe”); exit(EXIT_FAILURE); } snprintf(read_fd,sizeof(read_fd),”%d”,fd[0]); snprintf(write_fd,sizeof(write_fd),”%d”,fd[1]); fgets(line,sizeof(line),stdin); if (sscanf(line,”%d”,&number)>0) { if (fork()==0) { if (execl(“child”,”child”,read_fd,write_fd,NULL)<0) { write(fd[1],&number,sizeof(number)); wait(&st); read(fd[0],&result,sizeof(result)); printf(“%d x %d = %d\n”,number,number,result); close(fd[0]); close(fd[1]); return EXIT_SUCCESS; child.c 整数を文字列へ int main(int argc,char *argv[]) { int number,result; int read_fd, write_fd; // 引数の文字列を整数に変換 read_fd=atoi(argv[1]); write_fd=atoi(argv[2]); //パイプラインからデータを読込む read(read_fd,&number,sizeof(number)); result=number*number; //パイプラインにデータを書込む write(write_fd,&result,sizeof(result)); close(read_fd); close(write_fd); return EXIT_SUCCESS; } 引数はパイプのfd 標準入力から 数字をgetする 文字列を整数へ 引数はパイプのfd パイプバッファに書込む パイプバッファから読出す
signal シグナルを受け取ったときの処理を指定 3.1 シグナル通信 timer.c #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> time_t start; int main() { void stop(); time(&start); signal(SIGTSTP,stop); while (1) { sleep(1); printf("."); fflush(stdout); } return EXIT_FAILURE; void stop() { time_t end; time(&end); printf("elapsed time= %ld seconds\n",end-start); exit(EXIT_SUCCESS); signal シグナルを受け取ったときの処理を指定 インクルードファイル #include <signal.h> 書式 sighandler_t signal(int signum, sighandler_t sighandler); 引数 signum シグナルの種類 sighandler シグナルハンドラのアドレス 戻値 成功時 前回のシグナルハンドラ のアドレス 失敗時 SIG_ERR SIGTSTPを受信 したら関数stop()を実行する設定 標準出力(stdout)へ直ぐに出力 ctrl-Z [oida@rpc261 soft2]$ gcc timer.c -o timer [oida@rpc261 soft2]$ ./timer ...........elapsed time= 11 seconds [oida@rpc261 soft2]$
alarmシステムコールからのタイマーシグナル 3.2 シグナルの種類 Signalシステムコール signal(signum,sighandler); デフォルト動作: sighandler=SIG_DFL シグナルを無視: sighandler=SIG_IGN 種類 発生原因 デフォルト動作 SIGHUP 端末のハングアップ 終了 SIGINT Ctrl-C SIGQUIT Ctrl-\ コアダンプ SIGILL 不正な命令 SIGTRAP トレース SIGABRT abort関数の実行 SIGBUS バスエラー SIGFPE 浮動小数点例外 SIGKILL killシグナル SIGUSR1 ユーザ定義シグナル SIGSEGV 不正なメモリ参照 SIGUSR2 SIGPIPE バイプの破壊 SIGALRM alarmシステムコールからのタイマーシグナル SIGTERM 終了シグナル SIGCLD 子プロセスの停止、終了 無視 SIGCONT いったん停止からの再開 SIGSTOP プロセスのいったん停止 停止 SIGTSTP Ctrl-Z [oida@rpc261 soft2]$ cat > gomi a b c [1]+ Stopped cat >gomi [oida@rpc261 soft2]$ fg cat >gomi d e f [oida@rpc261 soft2]$ cat gomi [oida@rpc261 soft2]$ ctrl-Z 再開 ctrl-C
親の停止を防ぐため、ctrl-Zを無視する 宿題6 宿題:(parent.c,child.c,timer.cを参考にして)子プロセスはsleep時間をカウントし、シグナルSIGTSTP受信後、親プロセスにsleep時間をパイプラインで通知する。親プロセスは、 sleep時間をプリントして終了(親:hw6-p.c、子:hw6-c.c)。 表紙に氏名と学籍番号を書く 本文にプログラムを書く プログラムの実行結果を書く 実施した内容を説明する文章を書く レポートの締切は次の週の水曜日18:00 親 ( hw6-p.c ) 子 ( hw6-c.c ) 親の停止を防ぐため、ctrl-Zを無視する pipe() fork() execl("hw6-c") signal(SIGTSTP,SIG_IGN) Ctlr-Z write() wait() exit() read() printf()
宿題のヒント hw6-p.c hw6-c.c この部分を考えて下さい int main() { インクルード文を追加する #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <time.h> 宿題のヒント インクルード文を追加する #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> hw6-p.c hw6-c.c int main() { char read_fd[3],write_fd[3]; int fd[2],st; time_t result; if (pipe(fd)<0) { perror("pipe"); exit(EXIT_FAILURE); } snprintf(read_fd,sizeof(read_fd),"%d",fd[0]); snprintf(write_fd,sizeof(write_fd),"%d",fd[1]); if (fork()==0) { if (execl("hw6-c","hw6-c",read_fd,write_fd,NULL)<0) { } else { signal(SIGTSTP,SIG_IGN); // 親は Ctrl-Zを無視 wait(&st); read(fd[0],&result,sizeof(result)); printf("elapsed time = %ld\n",result); close(fd[0]); close(fd[1]); return EXIT_SUCCESS; time_t start; int read_fd,write_fd; int main(int argc,char *argv[]) { void stop(); read_fd=atoi(argv[1]); write_fd=atoi(argv[2]); signal(SIGTSTP,stop); time(&start); while (1) { sleep(1); printf(":"); fflush(stdout); } void stop() { time_t end; close(read_fd); close(write_fd); exit(EXIT_SUCCESS); この部分を考えて下さい Ctrl-Z後に子が行うことを書く 1)終了時刻(end)を測定 2)end-startをパイプバッファに書く