MENU

【C++】パイプによるプロセス間通信について~概要とサンプルコード~

この記事では、パイプを用いたプロセス間通信について紹介します。

パイプによるプロセス間通信は、Unix系システムでよく使われる方法の一つです。

パイプを使うことで、プロセス間でデータをやり取りすることができます。ここでは、C++でのパイプによるプロセス間通信の概要と簡単な実装例を紹介します。

目次

パイプの概要

パイプは、Unix系システムで提供されるプロセス間通信の手法の一つで、データのストリームを作成することによって、異なるプロセス間でデータをやり取りするために使用されます。

パイプの特性

一方向通信

パイプは一方向の通信チャネルを提供します。つまり、データはパイプの一方の端から書き込まれ、他方の端から読み取られます。

双方向通信が必要な場合は、2つのパイプ(1つはプロセスAからプロセスBへの通信用、もう1つはプロセスBからプロセスAへの通信用)を作成する必要があります。

親子プロセス間での使用

パイプは親プロセスが子プロセスを生成する際に作成され、親プロセスと子プロセス間で使用されることが多いです。

forkシステムコールを使うことで、親プロセスと子プロセスは同じパイプを共有します。

匿名パイプと名前付きパイプ

匿名パイプは、関連するプロセス間でのみ使用され、システム内の他のプロセスには見えませんpipeシステムコールを使って作成します。

名前付きパイプ(FIFO)は、ファイルシステム上に存在し、名前を持つため、関連するプロセス以外のプロセスからもアクセスできますmkfifoコマンドまたはmkfifoシステムコールを使って作成します。

パイプの基本操作

パイプの作成

pipe関数を使用して、パイプを作成します。

pipe_fd[0]は読み込み用のファイルディスクリプター、pipe_fd[1]は書き込み用のファイルディスクリプターです。

データの書き込みと読み込み

write関数を使ってパイプにデータを書き込みます。

read関数を使ってパイプからデータを読み込みます。

パイプの閉鎖

使用が終わったら、close関数を使ってパイプのファイルディスクリプターを閉じます。

実装例

親プロセスが子プロセスにメッセージを送り、子プロセスがそのメッセージを読み取って表示するプログラムを書いてみます。

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <cstring>

int main() {
    int pipe_fd[2];
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        return 1;
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        // 子プロセス
        close(pipe_fd[1]);  // 書き込み端を閉じる
        char buffer[100];
        ssize_t count = read(pipe_fd[0], buffer, sizeof(buffer) - 1);
        if (count == -1) {
            perror("read");
            return 1;
        }
        buffer[count] = '\0';
        std::cout << "子プロセスが受け取ったメッセージ: " << buffer << std::endl;
        close(pipe_fd[0]);
    } else {
        // 親プロセス
        close(pipe_fd[0]);  // 読み込み端を閉じる
        const char* message = "こんにちは、子プロセス!";
        if (write(pipe_fd[1], message, strlen(message)) == -1) {
            perror("write");
            return 1;
        }
        close(pipe_fd[1]);
        wait(nullptr);  // 子プロセスの終了を待つ
    }

    return 0;
}

ヘッダファイルのインクルード

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <cstring>
iostream

標準入力出力の使用

unistd.h

Unix標準APIの使用(pipeforkなど)

sys/wait.h

waitシステムコールの使用

cstring

文字列操作の使用

パイプの作成

int main() {
    int pipe_fd[2];
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        return 1;
    }

ここでは、パイプを作成しています。

pipe_fdは2つのファイルディスクリプタを持つ配列で、pipe_fd[0]は読み取り用、pipe_fd[1]は書き込み用です。

pipe関数が失敗した場合、エラーメッセージを表示し、プログラムを終了します。

プロセスの分岐

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }

fork関数を使ってプロセスを分岐します。forkが失敗した場合、エラーメッセージを表示し、プログラムを終了します。

子プロセスの処理

    if (pid == 0) {
        // 子プロセス
        close(pipe_fd[1]);  // 書き込み端を閉じる
        char buffer[100];
        ssize_t count = read(pipe_fd[0], buffer, sizeof(buffer) - 1);
        if (count == -1) {
            perror("read");
            return 1;
        }
        buffer[count] = '\0';
        std::cout << "子プロセスが受け取ったメッセージ: " << buffer << std::endl;
        close(pipe_fd[0]);
    } else {

子プロセスは読み取りのみを行うため、pipe_fd[1](書き込み端)を閉じます。

read関数を使って、pipe_fd[0](読み取り端)からデータを読み取ります。

読み取ったデータをbufferに格納し、終端文字('\0')を追加して文字列として扱います。

メッセージを標準出力に表示し、pipe_fd[0]を閉じます。

親プロセスの処理

        // 親プロセス
        close(pipe_fd[0]);  // 読み込み端を閉じる
        const char* message = "こんにちは、子プロセス!";
        if (write(pipe_fd[1], message, strlen(message)) == -1) {
            perror("write");
            return 1;
        }
        close(pipe_fd[1]);
        wait(nullptr);  // 子プロセスの終了を待つ
    }

    return 0;
}

親プロセスは書き込みのみを行うため、pipe_fd[0](読み取り端)を閉じます。

write関数を使って、pipe_fd[1](書き込み端)にメッセージを書き込みます。

メッセージを書き込んだ後、pipe_fd[1]を閉じます。

wait関数を使って子プロセスの終了を待ちます。

実行結果

作成したプログラムを実行すると、コンソールに以下のメッセージが出力されます。

パイプを使って親プロセスから子プロセスに、データを送信できていることが確認できます。

子プロセスが受け取ったメッセージ: こんにちは、子プロセス!
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

20代の組み込みソフトウェアエンジニア
主な使用言語はC++

---------------------資格---------------------
応用情報技術者
ネットワークスペシャリスト

目次