Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
パイプラインによるプロセス間通信を理解する
Search
Kotaro Kamashima
October 30, 2022
Programming
0
230
パイプラインによるプロセス間通信を理解する
社内向け勉強会に用意した資料「パイプラインによるプロセス間通信を理解する」です。
Kotaro Kamashima
October 30, 2022
Tweet
Share
More Decks by Kotaro Kamashima
See All by Kotaro Kamashima
Flutter Redux の内部構造を考える
kotaro666
0
190
Other Decks in Programming
See All in Programming
Vanilla JavaScript はマルチプラットフォームの夢を見るか
mitsuharu
0
120
Good first issues of TypeProf
mame
4
2.9k
Containerization Tips and Tricks for PHP apps
dunglas
2
3.4k
RAGのretrievalの評価を “ranx”で行う / Evaluate retrival of RAG using "ranx"
kun432
1
190
勉強会4_アップデートされたAssistantsAPIを試す
milky04
0
1.7k
RubyKaigi参加歴をふりかえる / Looking Back on My RubyKaigi Participation History #kaigieffectLT
expajp
2
210
製造業ドメインにOneTeamでディープ・ダイブする組織設計・組織運営
i_senaz
1
190
How to implement a RubyVM with PHP?
memory1994
PRO
3
1.4k
gRPCでの効率的なAPI開発とテストの進め方
uo
2
170
Good intentions gone bad
evaferreira
0
100
Swift Attributes
hokuron
0
120
スタックトレース始めてみた
kuro_kurorrr
5
1.3k
Featured
See All Featured
Unsuck your backbone
ammeep
664
57k
Imperfection Machines: The Place of Print at Facebook
scottboms
261
12k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
12
1.1k
Building Better People: How to give real-time feedback that sticks.
wjessup
356
18k
Designing the Hi-DPI Web
ddemaree
276
33k
Side Projects
sachag
451
41k
Docker and Python
trallard
35
2.8k
Automating Front-end Workflow
addyosmani
1357
200k
Faster Mobile Websites
deanohume
300
30k
Why Our Code Smells
bkeepers
PRO
331
56k
Bootstrapping a Software Product
garrettdimon
PRO
302
110k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
352
28k
Transcript
© Nzigen, Inc. パイプラインによるプロセス間通信を理解する 釜島 光太郎
本日のアジェンダ
本日のアジェンダ 1. ゴール 2. 前提知識 a. ファイルディスクリプタとは? b. 標準入出力と標準エラー出力とは?
c. パイプライン実行時のデータの流れを概略 d. forkを使って子プロセスを作る 3. パイプラインを使ったプロセス間通信 a. 子プロセスの処理 b. 親プロセスの処理
ゴール
ゴール パイプラインを伴ったコマンドを実行するとき、内部ではどのような動作が行われているのかを理解する $ cat access.log | grep “TARGET”
前提知識
前提知識 1. ファイルディスクリプタとは? 2. 標準入出力と標準エラー出力とは? 3. パイプライン使用時のデータの流れ 4. forkを使って子プロセスを作る
前提知識 ファイルディスクリプタ
ファイルディスクリプタ • プログラムがファイル等の外部のリソースにアクセスし たり、アクセスされたりする際に使われる抽象的な概 念 • UNIX/Linux系では、このファイルディスクリプタがファイル だけでなく、ソケットやパイプラインにも使われている
• この抽象的なファイルディスクリプタを使って、ファイルの 中身を見たり、ファイルに書き込んだりする • ファイルディスクリプタは小さい整数値から割り当てられる • OS標準規格では、0~2がすでに使用されているため、通 常は3から順番に割り当てられる • プログラム内で使用できるファイルディスクリプタの上限が 決まっている #define FILE_NAME "infile.txt" int main(void) { int fd; fd = open(FILE_NAME, O_RDONLY); // 読み込み専用でファイルを開く if (fd == -1) // ファイルを開くのに失敗した場合 { // エラー対応 } printf("File Descriptor = %d\n", fd); return (0); } bash-3.2$ ls Makefile ex00 infile.txt main.c bash-3.2$ ./ex00 File Descriptor = 3 サンプルプログラムはこちら
ファイルディスクリプタ • ulimit -n を使って、使用できるファイルディスクリプタ の上限を確認できる • 第2引数に整数値を渡すことで、現在のプロセス内の ファイルディスクリプタ上限値を変更できる
• 左の例は、ファイルディスクリプタの上限値を3に変更 したため、プログラム内でファイルを開けないエラーが 発生している bash-3.2$ ulimit -n 256 bash-3.2$ ulimit -n 3 bash-3.2$ ./ex00 open: Too many open files
前提知識 標準入出力と標準エラー出力
標準入出力と標準エラー出力 標準入力(stdin) ファイルディスクリプタ : 0 標準出力(stdout) ファイルディスクリプタ : 1
標準エラー出力(stderr) ファイルディスクリプタ : 2
標準入出力と標準エラー出力 1. プログラムが起動する 2. 無限ループ状態にする 3. 標準入力を待つ 4. 標準入力データを読み込む
5. 読み込んだデータを標準出力する 6. 2 に戻る
標準入出力と標準エラー出力 #define MAX_BYTES 100 // 最大読み込みバイト数の定義 int main(void) //
① プログラムが起動する { ssize_t bytes; // 読み込み成功した分のバイト数を格納する変数 char buffer[MAX_BYTES + 1]; // 読み込んだデータを格納する静的配列 while (1) // ② 無限ループ状態にする { // ③,④ 標準入力からデータを読み取り、バッファに格納する。 bytes = read(STDIN_FILENO, &buffer, MAX_BYTES); if (bytes == -1) // 読み込み失敗時の対応 { // エラーメッセージとともに標準エラー出力する } buffer[bytes] = '\0'; // 終端文字列としてヌル文字を代入する write(STDOUT_FILENO, buffer, bytes); // ⑤ バッファに格納されたデータを標準出力する bzero(buffer, bytes); // バッファの中を初期化する } return (0); } cat を使った標準入出力と標準エラー出力例 1. プログラムが起動する 2. 無限ループ状態にする 3. 標準入力を待つ 4. 標準入力データを読み込む 5. 読み込んだデータを標準出力する 6. 2 に戻る サンプルプログラムはこちら
前提知識 パイプライン使用時のデータの流れ
パイプライン実行時のデータの流れを概略 $ cat access.log | grep “TARGET” どういう処理が実行されるかイメージできますか?
パイプライン実行時のデータの流れを概略 $ cat access.log | grep “TARGET” ① ②
③ ④ ⑤ 1. ファイルを開いて、中身を読み込む 2. 読み込んだ内容をパイプラインを通じて、次のプログラム(プロセス)に渡す 3. パイプラインを通じて、流れてきたデータを読み込む 4. 渡された引数を検索文字列として、読み込んだデータを抽出する 5. 抽出したデータを標準出力する
パイプライン実行時のデータの流れを概略 $ cat access.log | grep “POST” を実行した時の例
前提知識 forkを使ったプロセス生成
forkを使ったプロセス生成 プロセスとは? • OS上で動いているプログラムまたはタスクのこと ◦ 皆さんが日常で使用しているChromeやGoogleChatアプリ、Gatheアプリも独立したプロセスとして動いている
◦ Linuxコマンドも一つひとつが独立したプロセスで実行されている ◦ 現在実行されているプロセスは、psコマンドやtopコマンドで確認できる • 各プロセスは独立して動いている ◦ 各プロセスは独立したメモリの中で動いている ◦ 独立して動いているので、一つのプロセスに致命的なバグが発生して緊急停止しても、他のプロセスには害を与えない • 実行されるプログラムを親として、forkを実行すると子プロセスが作られる ◦ 子プロセスは親プロセスの複製で作られる(fork) ◦ 親プロセスは子プロセスの状態を監視することができる • プロセス間でのデータのやり取りは大変だが、やる方法は何個かある ◦ シグナル通信 ◦ ソケット通信 ◦ セマフォ(プロセス間の排他制御) ◦ パイプライン(親子プロセス間で行う通信) → 今回のテーマ
forkを使ったプロセス生成 とある平日、親太郎が自宅でリモートワークをしていました。 そこに、子太郎が部屋に入ってきて、「お父さん遊ぼう!」と言いました。親太郎はまだ仕事中です。 そこで、マインクラフトでお城を作る(所要時間:3時間)ように子太郎に促し、その間自分は仕事をするようにしました。 そして、親太郎は仕事をしつつ、子供の遊びが終わるのを待ちます。子供の遊びが終わり、ちょうど仕事も終わったので、その後一緒にご飯を 食べに行きました
fork wait 遊び 仕事
forkを使ったプロセス生成 apache の例 子プロセス 子プロセス 子プロセス 子プロセス 子プロセス prefork
C10k問題
forkを使ったプロセス生成 int main(void) { pid_t pid; int status; printf("1.
Execute fork.\n"); pid = fork(); if (pid == -1) { // エラー処理 } else if (pid == 0) // 子プロセスの処理 { printf("2. Executing child process....\n"); sleep(2); printf("3. Finishing child process!\n"); exit(0); // 子プロセスを終了し、親に SIGCHILDシグナルが送信される } else // 親プロセスの処理 { wait(&status); // 子プロセスが終了するまで呼び出し側をブロックする printf("4. Executing parent process....\n"); } printf("5. Finishing program.\n"); return (0); } サンプルプログラムはこちら
パイプラインを使った プロセス間通信
パイプラインを使ったプロセス間通信 Q. プロセス間でデータを渡すためにどうするか? A. 糸電話のようにプロセス間にデータを橋渡しするものを作ってあげる
パイプラインを使ったプロセス間通信 int main(void) { int pipe_fd[2]; // 生成されたパイプラインの読み込み口と書き込み口のファイルディスクリプタが 格納される静的配列
pid_t pid; int status; pipe(pipe_fd); // パイプラインを作る return (0); } プロセス 0 1 2 pipe_fd[0] 3 4 pipe_fd[1] 標準エラー 標準出力 標準入力 書き込み 読み出し
パイプラインを使ったプロセス間通信 # define PIPE_READ_FD 0 # define PIPE_WRITE_FD 1
# define MAX_BYTES 100 int main(void) { int pipe_fd[2]; char buffer[MAX_BYTES + 1]; ssize_t bytes; pipe(pipe_fd); // パイプラインを生成する write(pipe_fd[PIPE_WRITE_FD], "This is test message.", 21); // パイプライン書き込み口に出力する bytes = read(pipe_fd[PIPE_READ_FD], &buffer, MAX_BYTES); // パイプライン書き込み口から読み込む if (bytes == -1) // 読み込み失敗時 { // エラー対応 } buffer[bytes] = '\0'; write(STDOUT_FILENO, buffer, bytes); // 読み込んだデータを標準出力する return (0); } プロセス 0 1 2 pipe_fd[0] 3 4 pipe_fd[1] 標準エラー 標準出力 標準入力 書き込み 読み出し サンプルプログラムはこちら
パイプラインを使ったプロセス間通信 子プロセス 0 1 2 3 4 標準入力 標準出力
標準エラー pipe_fd[0] pipe_fd[1] 親プロセス 0 1 2 3 4 標準入力 標準出力 標準エラー pipe_fd[0] pipe_fd[1] 書き出し 読み出し 書き出し 読み出し パイプライン pipeしてから、fork実行後の様子。この状態では、ちゃんとデータが期待通りに流れない。
パイプラインを使ったプロセス間通信 子プロセス 0 1 2 3 4 標準入力 標準出力
標準エラー pipe_fd[0] pipe_fd[1] 親プロセス 0 1 2 3 4 標準入力 標準出力 標準エラー pipe_fd[0] pipe_fd[1] 書き出し 読み出し 書き出し 読み出し パイプライン パイプライン内で出入り口を操作することで、データの通り道を決めてあげる
パイプラインを使ったプロセス間通信 int main(void) { int pipe_fd[2]; pid_t pid; int
status; pipe(pipe_fd); pid = fork(); if (pid == -1) // エラー対応 { // エラー対応 } else if (pid == 0) // 子プロセス { start_child_process(pipe_fd); } else // 親プロセス { wait(&status); // 子プロセスを待つ start_parent_process(pipe_fd); } return (0); } • main 関数の大まかな実装 • forkして子プロセスを作る • 子プロセス内で前半の処理 (cat access.log)を行う • 親プロセス内で後半の処理 (grep “TARGET”)を行う サンプルプログラムはこちら
子プロセスの処理
パイプラインを使ったプロセス間通信 int main(void) { int pipe_fd[2]; pid_t pid; int
status; pipe(pipe_fd); pid = fork(); if (pid == -1) // エラー対応 { // エラー対応 } else if (pid == 0) // 子プロセス { start_child_process(pipe_fd); } else // 親プロセス { wait(&status); // 子プロセスを待つ start_parent_process(pipe_fd); } return (0); } static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]); // パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { perror("open"); exit(1); } dup2(file_fd, STDIN_FILENO) // 標準入力を access.logファイルFDのコピーとして作 成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き 込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親に SIGCHILDが送信される }
パイプラインを使ったプロセス間通信 static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]);
// パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { // エラー処理 } dup2(file_fd, STDIN_FILENO) // 標準入力をaccess.logファイルFDのコピーとして作成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親にSIGCHILDが送信される } 子プロセス 0 1 2 4 pipe_fd[1] 標準エラー 標準出力 標準入力 書き込み 読み出し 3 pipe_fd[0]
パイプラインを使ったプロセス間通信 static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]);
// パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { // エラー処理 } dup2(file_fd, STDIN_FILENO) // 標準入力をaccess.logファイルFDのコピーとして作成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親にSIGCHILDが送信される } 子プロセス 0 1 2 4 pipe_fd[1] 標準エラー 標準出力 標準入力 書き込み 読み出し file_fd 5 pipe_fd[0] 3
パイプラインを使ったプロセス間通信 static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]);
// パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { // エラー処理 } dup2(file_fd, STDIN_FILENO) // 標準入力をaccess.logファイルFDのコピーとして作成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親にSIGCHILDが送信される } 子プロセス 0 → access.log 1 2 4 pipe_fd[1] 標準エラー 標準出力 標準入力 書き込み 読み出し file_fd 5 3 pipe_fd[0]
パイプラインを使ったプロセス間通信 static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]);
// パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { // エラー処理 } dup2(file_fd, STDIN_FILENO) // 標準入力をaccess.logファイルFDのコピーとして作成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親にSIGCHILDが送信される } 子プロセス 0 → access.log 1 2 4 pipe_fd[1] 標準エラー 標準出力 標準入力 書き込み 読み出し pipe_fd[0] 3 file_fd 5
パイプラインを使ったプロセス間通信 static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]);
// パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { // エラー処理 } dup2(file_fd, STDIN_FILENO) // 標準入力をaccess.logファイルFDのコピーとして作成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親にSIGCHILDが送信される } 子プロセス 0 → access.log 1 → pipe_fd[1] 2 4 pipe_fd[1] 標準エラー 標準出力 標準入力 書き込み 読み出し 3 pipe_fd[0] file_fd 5
パイプラインを使ったプロセス間通信 static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]);
// パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { // エラー処理 } dup2(file_fd, STDIN_FILENO) // 標準入力をaccess.logファイルFDのコピーとして作成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親にSIGCHILDが送信される } 子プロセス 0 → access.log 1 → pipe_fd[1] 2 標準エラー 標準出力 標準入力 書き込み 読み出し pipe_fd[0] 3 pipe_fd[1] 4 5 file_fd
パイプラインを使ったプロセス間通信 static void start_child_process(int *pipe_fd) { int file_fd; close(pipe_fd[PIPE_READ_FD]);
// パイプライン読み込み口を閉じる file_fd = open("access.log", O_RDONLY); // access.logファイルを開く if (file_fd == -1) { // エラー処理 } dup2(file_fd, STDIN_FILENO) // 標準入力をaccess.logファイルFDのコピーとして作成する close(file_fd); // access.logファイルFDを閉じる dup2(pipe_fd[PIPE_WRITE_FD], STDOUT_FILENO) // 標準出力をパイプライン書き込み口のコピーとして作成する close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親にSIGCHILDが送信される } extern char **environ; // 設定されている環境変数 static void execute_child_command() { char *argv[2]; // char型2次元配列 argv[0] = "/bin/cat"; // 実行するコマンドの絶対パス argv[1] = NULL; // 2次元配列の一番後ろに NULLを代入する if (execve(argv[0], argv, environ) == -1) // プログラムを実行する { // エラー処理 } } #include <unistd.h> int execve(const char * filename , char *const argv [], char *const envp []); filename: プログラムファイルの絶対パス argv: プログラムに渡す引数の 2次元配列(NULLポインタで終端) envp: プログラムに渡す環境変数の 2次元配列(NULLポインタで終端)
パイプラインを使ったプロセス間通信 子プロセス 0 → access.log 1 → pipe_fd[1] 2
3 4 標準入力 標準出力 標準エラー pipe_fd[0] pipe_fd[1] 親プロセス 0 1 2 3 4 標準入力 標準出力 標準エラー pipe_fd[0] pipe_fd[1] 書き出し 読み出し 書き出し 読み出し パイプライン 子プロセスの処理実行時 file_fd 5
親プロセスの処理
パイプラインを使ったプロセス間通信 int main(void) { int pipe_fd[2]; pid_t pid; int
status; pipe(pipe_fd); pid = fork(); if (pid == -1) // エラー対応 { // エラー対応 } else if (pid == 0) // 子プロセス { start_child_process(pipe_fd); } else // 親プロセス { wait(&status); // 子プロセスを待つ start_parent_process(pipe_fd); } return (0); } static void start_parent_process(int *pipe_fd) { close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる dup2(pipe_fd[PIPE_READ_FD], STDIN_FILENO) // 標準入力をパイプライン読み込み口のコピーとして作成する close(pipe_fd[PIPE_READ_FD]); // パイプライン読み込み口を閉じる execute_parent_command(); // 親プロセスの処理を実行する }
パイプラインを使ったプロセス間通信 int main(void) { int pipe_fd[2]; pid_t pid; int
status; pipe(pipe_fd); pid = fork(); if (pid == -1) // エラー対応 { // エラー対応 } else if (pid == 0) // 子プロセス { start_child_process(pipe_fd); } else // 親プロセス { wait(&status); // 子プロセスを待つ start_parent_process(pipe_fd); } return (0); } static void start_child_process(int *pipe_fd) { …省略 execute_child_command(); // 子プロセスの処理を実行する exit(0); // 子プロセスを終了し、親に SIGCHILDが送信される } 子プロセス 親プロセス 終了 SIGCHILD waitの ブロッキング解除
テンプレート 親プロセス 0 1 2 3 4 標準入力 標準出力 標準エラー
pipe_fd[0] pipe_fd[1] 書き出し 読み出し static void start_parent_process(int *pipe_fd) { close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる dup2(pipe_fd[PIPE_READ_FD], STDIN_FILENO) // 標準入力をパイプライン読み込み口のコピーとして作成する close(pipe_fd[PIPE_READ_FD]); // パイプライン読み込み口を閉じる execute_parent_command(); // 親プロセスの処理を実行する }
テンプレート 親プロセス 0 → pipe_fd[0] 1 2 3 4 標準入力
標準出力 標準エラー pipe_fd[0] pipe_fd[1] 書き出し 読み出し static void start_parent_process(int *pipe_fd) { close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる dup2(pipe_fd[PIPE_READ_FD], STDIN_FILENO) // 標準入力をパイプライン読み込み口のコピーとして作成する close(pipe_fd[PIPE_READ_FD]); // パイプライン読み込み口を閉じる execute_parent_command(); // 親プロセスの処理を実行する }
テンプレート 親プロセス 0 → pipe_fd[0] 1 2 3 4 標準入力
標準出力 標準エラー pipe_fd[0] pipe_fd[1] 書き出し 読み出し static void start_parent_process(int *pipe_fd) { close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる dup2(pipe_fd[PIPE_READ_FD], STDIN_FILENO) // 標準入力をパイプライン読み込み口のコピーとして作成する close(pipe_fd[PIPE_READ_FD]); // パイプライン読み込み口を閉じる execute_parent_command(); // 親プロセスの処理を実行する }
パイプラインを使ったプロセス間通信 static void start_parent_process(int *pipe_fd) { close(pipe_fd[PIPE_WRITE_FD]); // パイプライン書き込み口を閉じる
dup2(pipe_fd[PIPE_READ_FD], STDIN_FILENO) // 標準入力をパイプライン読み込み口のコピーとして作成する close(pipe_fd[PIPE_READ_FD]); // パイプライン読み込み口を閉じる execute_parent_command(); // 親プロセスの処理を実行する } static void execute_parent_command() { char *argv[3]; // char型2次元配列 argv[0] = "/usr/bin/grep"; // 実行するコマンドの絶対パス argv[1] = "POST"; // grep で使用する引数 argv[2] = NULL; // 2次元配列の一番後ろに NULLを代入する if (execve(argv[0], argv, NULL) == -1) // プログラムを実行する { // エラー処理 } } #include <unistd.h> int execve(const char * filename , char *const argv [], char *const envp []); filename: プログラムファイルの絶対パス argv: プログラムに渡す引数の 2次元配列(NULLポインタで終端) envp: プログラムに渡す環境変数の 2次元配列(NULLポインタで終端)
パイプラインを使ったプロセス間通信 子プロセス 0 → access.log 1 → pipe_fd[1] 2
3 4 標準入力 標準出力 標準エラー pipe_fd[0] pipe_fd[1] 親プロセス 0 → pipe_fd[0] 1 2 3 4 標準入力 標準出力 標準エラー pipe_fd[0] pipe_fd[1] 書き出し 読み出し 書き出し 読み出し パイプライン 親プロセスの処理実行時 file_fd 5
パイプラインを使ったプロセス間通信 bash-3.2$ make gcc main.c -o ex04 bash-3.2$ ls
Makefile README.md access.log ex04 main.c bash-3.2$ ./ex04 5.78.198.52 - - [22/Jan/2019:03:56:26 +0330] "POST /m/updateVariation?__amp_source_origin=https%3A%2F%2Fwww.zanbil.ir HTTP/1.1" 200 171 "https://www.zanbil.ir/m/product/33978/64784/%DA%AF%D9%88%D8%B4%DB%8C-%D9%85%D9%88%D8%A8%D8%A7%DB%8C%D9%84-% D8%B3%D8%A7%D9%85%D8%B3%D9%88%D9%86%DA%AF-%D9%85%D8%AF%D9%84-Galaxy-A9-%282018%29-Dual-128GB-%28SM-A9 20%29" "Mozilla/5.0 (Linux; Android 8.0.0; SAMSUNG SM-G950F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/8.2 Chrome/63.0.3239.111 Mobile Safari/537.36" "-" 5.78.198.52 - - [22/Jan/2019:03:56:28 +0330] "POST /m/updateVariation?__amp_source_origin=https%3A%2F%2Fwww.zanbil.ir HTTP/1.1" 200 171 "https://www.zanbil.ir/m/product/33978/64784/%DA%AF%D9%88%D8%B4%DB%8C-%D9%85%D9%88%D8%A8%D8%A7%DB%8C%D9%84-% D8%B3%D8%A7%D9%85%D8%B3%D9%88%D9%86%DA%AF-%D9%85%D8%AF%D9%84-Galaxy-A9-%282018%29-Dual-128GB-%28SM-A9 20%29" "Mozilla/5.0 (Linux; Android 8.0.0; SAMSUNG SM-G950F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/8.2 Chrome/63.0.3239.111 Mobile Safari/537.36" "-" プログラム実行時の様子
ご清聴 ありがとうございました