子プロセスの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 追記
ブロッキングの問題を考慮して、ノンブロッキングで読むようにした。