[ C言語 ] プロセスの生成 ( fork ) と パイプによるプロセス間通信 ( pipe )

Pocket

Unix / Linux で C 言語を用いてプロセスを新規に作成する場合には、システムコール fork を使用します。また、fork システムコールによって生成したプロセスとパイプによるプロセス間通信を行うことができます、ここでは、そのサンプルコードを掲載しています。詳細はコメントを参照ください。

スポンサーリンク

fork システムコールによるプロセスの生成
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char** argv)
{
    pid_t pid;
    int status;

    char* str = "test string";

    // プロセスを作成する
    pid = fork();

    // プロセスの作成に失敗
    if ( pid < 0 ) {
        perror("fork()");
        return -1;
    }

    // 子プロセスである場合は、0がリターンされる
    if (pid == 0) {
        printf("I am child process [%d]\n", getpid());
        // この時点では親も子も関係なく、参照可能
        printf("I am child process [%s]\n", str);

        // プロセスを乗せかえる
        execl("/bin/ls", "ls", (char *)NULL);

        // これ以降、実行されない。プロセスが置き換えられている。
        printf("not reached\n");
        usleep(10000000);

    } else { // 親プロセスの場合は、作成した子プロセスのPIDがリターンされる

        printf("I am parent process [%d]\n", getpid());
        printf("I am parent process [%s]\n", str);

        // 子プロセスの終了を待つ
        waitpid(pid, &status, 0);
        printf ("child process (PID = %d) finished\n", pid);
    }

    return 0;
}

実行結果

$ # コンパイル
$ gcc fork.c
$
$ # 実行
$ ./a.out
I am parent process [12474]
I am parent process [test string]
I am child process [12475]
I am child process [test string]
child process (PID = 12475) finished
ポイント
  • fork はプロセスの生成というよりも現在のプロセスの複製を作成する(変数 str が子プロセスでも出力できていることで確認できる)
  • 動作させたいプロセスは、exec によるプロセスの乗せかえで実現する
  • プロセス乗せかえ後の処理は実行されない

次に、パイプを使用したプロセス間通信のサンプルコードになります。pipe システムコールを使用しています。

パイプを使用したプロセス間通信のサンプルコード
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char** argv)
{
    int         fds[2];
    pid_t       pid;
    int         status;
    char        buf[32];

    memset(buf, 0, sizeof(buf)); // バッファの初期化

    // パイプを作成
    if (pipe(fds) < 0) {
        perror("pipe()");
        return -1;
    }

    // プロセスの複製を作成する
    if ((pid = fork()) < 0) {
        perror("fork()");
        return -1;
    }

    if (pid == 0) { // 子プロセス
        // パイプから読み込む
        if(read(fds[0], buf, sizeof(buf)) < 0) {
            perror("read()");
            return -1;
        }
        printf("%s, child process received\n", buf);

        // パイプへ出力
        if(write(fds[1], "hello parent", 12) < 0) {
            perror("write()");
            return -1;
        }

        // パイプをクローズ
        close(fds[0]); // 入力
        close(fds[1]); // 出力

    } else {   // 親プロセス

        // パイプへ出力
        if(write(fds[1], "hello child", 11) < 0) {
            perror("write()");
            return -1;
        }

        sleep(1); // スリープで実行順序を制御

        // パイプから読み込む
        if(read(fds[0], buf, sizeof(buf)) < 0) {
            perror("read()");
            return -1;
        }
        printf("%s, parent process received\n", buf);

        close(fds[0]);
        close(fds[1]);

        // 子プロセスの終了を待つ
        waitpid(pid, &status, 0);
        printf ("child process (PID = %d) finished\n", pid);
    }

    return 0;
}

実行結果

$ ./pipe_sample
hello child, child process received
(1秒待機)
hello parent, parent process received
child process (PID = 5724) finished
サンプルコードの問題点

上記に示したサンプルコードでは、スリープで実行順序を制御させている点です。 これは、親プロセスの出力は、子プロセスの入力だけでなく、親プロセス自身の入力にも接続されているので、 親プロセスが受け取るか、子プロセスが受け取るかがわからないための対処となります。

実用的なコードでは、プロセス間の排他制御を実装するか、一方的な通信として不要なディスクリプタを 閉じてしまう方法をとることが多いです。以下に、一方方向の通信を行うサンプルコードを示します(変更点のみ)。

    if (pid == 0) { // 子プロセス

        close(fds[1]); // 出力用ディスクリプタを閉じる

        // パイプから読み込む
        if(read(fds[0], buf, sizeof(buf)) < 0) {
            perror("read()");
            return -1;
        }
        printf("%s, child process received\n", buf);

        // パイプをクローズ
        close(fds[0]); // 入力

    } else {   // 親プロセス

        close(fds[0]); // 入力用ディスクリプタを閉じる

        // パイプへ出力
        if(write(fds[1], "hello child", 11) < 0) {
            perror("write()");
            return -1;
        }

        close(fds[1]);

        // 子プロセスの終了を待つ
        waitpid(pid, &status, 0);
        printf ("child process (PID = %d) finished\n", pid);
    }
参考

 

スポンサーリンク


Pocket

One thought on “[ C言語 ] プロセスの生成 ( fork ) と パイプによるプロセス間通信 ( pipe )”

  1. Pingback: C言語 プロセスの実行と出力結果の取得 ( popen ) – 偏差値40の高い壁

Leave a Comment

Your email address will not be published. Required fields are marked *