4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

名前付きパイプとdup2

Last updated at Posted at 2016-03-28

子プロセスのstdoutとstderrを、名前付きパイプにつないで、
子プロセスの出力結果を親プロセスで読む。

以下の例では、子プロセス自体でstdoutとstderrに文字列を送って、
その後execし、別のプログラムを実行している。

a.c
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/wait.h>
# include <fcntl.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>

const char Fifo[] = "/tmp/test.fifo";

void child() {

    // テストとして、5秒待っている。
    // ここで5秒待っているが、親プロセスの
    // openの処理が即座に終わっていることを確認して欲しい。
    // 仮に、親プロセスのopenを、ブロッキングモードで開いた場合、
    // 親プロセスのopenは、この5秒を待ってから終了する。
    sleep(5);

    printf("[%d]: fopen start.\n", getpid());
    FILE* fp = fopen(Fifo, "w");
    printf("[%d]: fopen end.\n", getpid());

    dup2(fileno(fp), STDOUT_FILENO);
    dup2(fileno(fp), STDERR_FILENO);
    fclose(fp);

    write(STDOUT_FILENO, "stdout test.\n", 13);
    write(STDERR_FILENO, "stderr test.\n", 13);

    execl("/bin/cat", "/bin/cat", "/tmp/test.txt", NULL);
    exit(1);
}

int main(void) {

    mkfifo(Fifo, 0666);

    pid_t pid = fork();

    if (pid == 0) {
        child();
    } else if(pid < 0) {
        return 1;
    }

    int fd;

    printf("[%d]: open start.\n", getpid());

    // openの処理は、O_NONBLOCKを渡すと、待たずに返ってくる。
    // O_NONBLOCKなしでopenした場合、書き込み側がopenしてくれるまで
    // ここでブロックされることになる。
    // 仮に、読み込み側が先にopenし、書き込み側が、
    // 何かの理由でopen出来なかった場合、デッドロックする。
    // 読み込み側も、openできなかった場合は書き込み側にkillを投げる等
    // 対策が必要。とにかくデッドロックの危険があることは意識すること。
    fd = open(Fifo, O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        printf("%s\n", strerror(errno));
        kill(pid, SIGKILL); // 本当はちゃんと、最初はterm投げてから、、とかやったほうが良い。。
        waitpid(pid, NULL, 0);
        return 1;
    }

    printf("[%d]: open end.\n", getpid());

    char msg[256];

    // 子をwaitpidで回収できた後、
    // readをした結果が0以下となるなら、
    // もう読むものは無いので、終了する。

    // 以下の結果の回収ループは、子が即座に終了した場合でも、
    // 1秒は待ってから終了する。

    // 子の開始と終了の時間がシビアに必要になる場合、
    // ノンブロッキングの処理をせずに、
    // デッドロックが起きないよう、細心の注意を払いながら
    // プログラムを書く必要がある。pollやselectを使って。

    while (1) {
        ssize_t r = read(fd, msg, 256);
        if (r > 0) {
            msg[r] = '\0';
            printf("msg=%s", msg);
        } else {
            if (pid == -1) {
                break;
            }
            if (waitpid(pid, NULL, WNOHANG) == pid) {
                pid = -1;
            }
            sleep(1);
        }
    }

    close(fd);

    return 0;
}


[root@todanano ~]#
[root@todanano ~]# gcc a.c
[root@todanano ~]# ./a.out
[14417]: open start.
[14417]: open end.
[14418]: fopen start.
[14418]: fopen end.
msg=stdout test.
stderr test.
/bin/cat: /tmp/test.txt: No such file or directory
[root@todanano ~]#
[root@todanano ~]#
[root@todanano ~]# echo "abc123" > /tmp/test.txt
[root@todanano ~]#
[root@todanano ~]# ./a.out
[14421]: open start.
[14421]: open end.
[14422]: fopen start.
[14422]: fopen end.
msg=stdout test.
stderr test.
abc123
[root@todanano ~]# ll /tmp/test.fifo
prw-r--r-- 1 root root 0 Apr  1 17:35 /tmp/test.fifo
[root@todanano ~]#
[root@todanano ~]#
[root@todanano ~]#

やっていることを見やすくするために、諸々エラーチェックを省いているので、
実際に使うときは、必要に応じてエラーチェックしてください。

2016/04/01 追記
ブロッキングの問題を考慮して、ノンブロッキングで読むようにした。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?