プロセス間通信-パイプ
パイプは、2つ以上の関連または相互に関連するプロセス間の通信媒体です。これは、1つのプロセス内、または子プロセスと親プロセス間の通信のいずれかになります。通信は、親、子、孫の間の通信など、マルチレベルにすることもできます。通信は、パイプに書き込む1つのプロセスと、パイプから読み取る他のプロセスによって実現されます。パイプシステムコールを実現するには、2つのファイルを作成します。1つはファイルに書き込むためのもので、もう1つはファイルから読み取るためのものです。
パイプのメカニズムは、バケツなどのコンテナにパイプで水を入れたり、マグカップなどで誰かがそれを回収したりするなど、リアルタイムのシナリオで表示できます。充填プロセスはパイプへの書き込みに他ならず、読み取りプロセスはパイプからの取得に他なりません。これは、一方の出力(水)がもう一方の出力(バケツ)に入力されることを意味します。
#include<unistd.h>
int pipe(int pipedes[2]);
このシステムコールは、一方向通信用のパイプを作成します。つまり、2つの記述子を作成します。最初の記述子はパイプからの読み取りに接続され、もう1つはパイプへの書き込みに接続されます。
記述子pipedes [0]は読み取り用で、pipedes [1]は書き込み用です。pipedes [1]に書き込まれたものはすべて、pipedes [0]から読み取ることができます。
この呼び出しは、成功した場合は0を返し、失敗した場合は-1を返します。失敗の原因を知るには、errno変数またはperror()関数で確認してください。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
ファイルの基本的な操作は読み取りと書き込みですが、操作を実行する前にファイルを開き、必要な操作の完了後にファイルを閉じることが不可欠です。通常、デフォルトでは、プロセスごとに3つの記述子が開かれ、ファイル記述子0、1、および2を持つ入力(標準入力– stdin)、出力(標準出力– stdout)、およびエラー(標準エラー– stderr)に使用されます。
このシステムコールは、読み取り/書き込み/シーク(lseek)の以降のファイル操作に使用されるファイル記述子を返します。通常、ファイル記述子は3から始まり、開くファイルの数に応じて1つずつ増加します。
オープンシステムコールに渡される引数は、パス名(相対パスまたは絶対パス)、ファイルを開く目的(たとえば、読み取り用に開く、O_RDONLY、書き込み用、O_WRONLY、読み取りと書き込み用、O_RDWR、既存のファイルに追加する)を示すフラグです。 O_APPEND、ファイルを作成する(O_CREATなどで存在しない場合)、およびユーザーまたは所有者/グループ/その他に読み取り/書き込み/実行のアクセス許可を提供する必要なモード。モードは記号で表すことができます。
読み取り– 4、書き込み– 2、実行–1。
例:8進値(0で始まる)、0764は、所有者が読み取り、書き込み、および実行のアクセス許可を持っていること、グループが読み取りおよび書き込みのアクセス許可を持っていること、その他が読み取りアクセス許可を持っていることを意味します。これは、S_IRWXU |として表すこともできます。S_IRGRP | S_IWGRP | S_IROTH、これは0700 | 0040 | 0020 | 0004→0764の操作を意味します。
このシステムコールは、成功すると、新しいファイル記述子IDを返し、エラーの場合は-1を返します。エラーの原因は、errno変数またはperror()関数で特定できます。
#include<unistd.h>
int close(int fd)
上記のシステムコールは、すでに開いているファイル記述子を閉じます。これは、ファイルが使用されなくなり、関連付けられたリソースを他のプロセスで再利用できることを意味します。このシステムコールは、成功した場合はゼロを返し、エラーの場合は-1を返します。エラーの原因は、errno変数またはperror()関数で特定できます。
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t count)
上記のシステムコールは、ファイル記述子fdの引数、割り当てられたメモリ(静的または動的)を備えた適切なバッファ、およびバッファのサイズを使用して、指定されたファイルから読み取ることです。
ファイル記述子IDは、open()またはpipe()システムコールを呼び出した後に返されるそれぞれのファイルを識別するためのものです。ファイルから読み取る前に、ファイルを開く必要があります。pipe()システムコールを呼び出すと自動的に開きます。
この呼び出しは、成功した場合は読み取られたバイト数(または、ファイルの終わりに遭遇した場合はゼロ)を返し、失敗した場合は-1を返します。データが利用できない場合やファイルが閉じられている場合に備えて、返されるバイト数は要求されたバイト数よりも小さくすることができます。障害が発生した場合に備えて、適切なエラー番号が設定されます。
失敗の原因を知るには、errno変数またはperror()関数で確認してください。
#include<unistd.h>
ssize_t write(int fd, void *buf, size_t count)
上記のシステムコールは、ファイル記述子fdの引数、割り当てられたメモリ(静的または動的)を備えた適切なバッファ、およびバッファのサイズを使用して、指定されたファイルに書き込むことです。
ファイル記述子IDは、open()またはpipe()システムコールを呼び出した後に返されるそれぞれのファイルを識別するためのものです。
ファイルに書き込む前に、ファイルを開く必要があります。pipe()システムコールを呼び出すと自動的に開きます。
この呼び出しは、成功した場合は書き込まれたバイト数(または何も書き込まれていない場合はゼロ)を返し、失敗した場合は-1を返します。障害が発生した場合に備えて、適切なエラー番号が設定されます。
失敗の原因を知るには、errno変数またはperror()関数で確認してください。
サンプルプログラム
以下はいくつかのサンプルプログラムです。
Example program 1 −パイプを使用して2つのメッセージを読み書きするプログラム。
アルゴリズム
Step 1 −パイプを作成します。
Step 2 −パイプにメッセージを送信します。
Step 3 −パイプからメッセージを取得し、標準出力に書き込みます。
Step 4 −パイプに別のメッセージを送信します。
Step 5 −パイプからメッセージを取得し、標準出力に書き込みます。
Note −メッセージの取得は、すべてのメッセージを送信した後に実行することもできます。
Source Code: simplepipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 1 is %s\n", readmessage);
printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 2 is %s\n", readmessage);
return 0;
}
Note−理想的には、システムコールごとに返品ステータスを確認する必要があります。プロセスを簡素化するために、すべての呼び出しに対してチェックが行われるわけではありません。
実行手順
コンパイル
gcc -o simplepipe simplepipe.c
実行/出力
Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell
Example program 2 −親プロセスと子プロセスを使用して、パイプを介して2つのメッセージを読み書きするプログラム。
アルゴリズム
Step 1 −パイプを作成します。
Step 2 −子プロセスを作成します。
Step 3 −親プロセスがパイプに書き込みます。
Step 4 −子プロセスはパイプからメッセージを取得し、それを標準出力に書き込みます。
Step 5 −手順3と手順4をもう一度繰り返します。
Source Code: pipewithprocesses.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds[2];
int returnstatus;
int pid;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
// Child process
if (pid == 0) {
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
} else { //Parent process
printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
}
return 0;
}
実行手順
Compilation
gcc pipewithprocesses.c –o pipewithprocesses
Execution
Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello
パイプを使用した双方向通信
パイプ通信は、一方向の通信のみと見なされます。つまり、親プロセスが書き込みを行い、子プロセスが読み取りを行うか、またはその逆であり、両方ではありません。ただし、親と子の両方がパイプからの書き込みと読み取りを同時に行う必要がある場合、解決策はパイプを使用した双方向通信です。双方向通信を確立するには、2本のパイプが必要です。
以下は、双方向通信を実現するための手順です。
Step 1−2つのパイプを作成します。1つ目は、pipe1のように、親が書き込み、子が読み取りを行うためのものです。2つ目は、pipe2のように、子が書き込み、親が読み取りを行うためのものです。
Step 2 −子プロセスを作成します。
Step 3 −各通信に必要な端は1つだけなので、不要な端を閉じます。
Step 4 −親プロセスの不要な端を閉じ、pipe1の端を読み取り、pipe2の端を書き込みます。
Step 5 −子プロセスの不要な端を閉じ、pipe1の端を書き込み、pipe2の端を読み取ります。
Step 6 −必要に応じて通信を行ってください。
サンプルプログラム
Sample program 1 −パイプを使用した双方向通信の実現。
アルゴリズム
Step 1 −親プロセスが書き込むためのpipe1と、子プロセスが読み取るためのpipe1を作成します。
Step 2 −子プロセスが書き込み、親プロセスが読み取りを行うためのpipe2を作成します。
Step 3 −親側と子側からパイプの不要な端を閉じます。
Step 4 −メッセージを書き込む親プロセスと、画面に読み取って表示する子プロセス。
Step 5 −メッセージを書き込む子プロセスと、画面に読み取って表示する親プロセス。
Source Code: twowayspipe.c
#include<stdio.h>
#include<unistd.h>
int main() {
int pipefds1[2], pipefds2[2];
int returnstatus1, returnstatus2;
int pid;
char pipe1writemessage[20] = "Hi";
char pipe2writemessage[20] = "Hello";
char readmessage[20];
returnstatus1 = pipe(pipefds1);
if (returnstatus1 == -1) {
printf("Unable to create pipe 1 \n");
return 1;
}
returnstatus2 = pipe(pipefds2);
if (returnstatus2 == -1) {
printf("Unable to create pipe 2 \n");
return 1;
}
pid = fork();
if (pid != 0) // Parent process {
close(pipefds1[0]); // Close the unwanted pipe1 read side
close(pipefds2[1]); // Close the unwanted pipe2 write side
printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
read(pipefds2[0], readmessage, sizeof(readmessage));
printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
} else { //child process
close(pipefds1[1]); // Close the unwanted pipe1 write side
close(pipefds2[0]); // Close the unwanted pipe2 read side
read(pipefds1[0], readmessage, sizeof(readmessage));
printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
}
return 0;
}
実行手順
コンパイル
gcc twowayspipe.c –o twowayspipe
実行
In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello