リダイレクトとパイプ連結
リダイレクト
リダイレクトは出力をファイルにする機能で、出力先のファイルディスクリプター0を、通常ファイルのファイルディスクリプターに書き換えることで実現できます。以下のサンプルはps > testをシュミレートしています。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
void ps();
int fd;
int main(void)
{
fd = open("test", O_WRONLY|O_CREAT);
if(fork() == 0)
ps();
wait();
}
void ps()
{
close(1);
dup(fd);
execlp("ps","ps", NULL);
}
まずtestファイルを書き込みモードで作成し、forkでプロセスを作成したのち、このファイルディスクリプターを標準出力であるファイルディスクリプター1に差し替えて、子プロセスをexeclpでpsコマンドに置き換えています。従って、起動されたpsコマンドの標準出力はtestに書き込む形で動作することになります。forkは親のプロセスディスクリプターを複写して子プロセスのディスクリプターを作成します。またCopyOnWriteにより、参照においてメモリを共有することになります。execlpは新規の仮想メモリー空間にpsをロードしますが、ファイルディスクリプターを初期化することはありません。従って子プロセスのps関数のfdは、親プロセスのopenのfdであり、そのinodeも同じものとなります。
リダイレクトとは子プロセスの標準出力ディスクリプター1に、リダイレクト先ファイルのファイルディスクリプターを差し替えることで実現できます。
p/s
dupは0から空いているディスクリプターを取得します。close(1)で1をクローズすることで、dup(fd)はディスクリプター1に差し替えられます。
パイプ連結
下記はps | cat をシュミレートするサンプルです。考え方はリダイレクトと同じです。リダイレクトでは実ファイルのファイルディスクリプターを取得していましたが、パイプではパイプのファイルディスクリプター読み込み用と書き込み用2つを取得します。ここで2つディスクリプター取得していますが、パイプそのものは1つだけで、読み込み用と書き込み用別々にファイルディスクリプターを取得しているだけです。そして、psコマンドの子プロセスと、catコマンドの子プロセスを作成し、psコマンドには書き込み用のファイルディスクリプターを標準出力に差し替えて、catコマンドには読み込み用のファイルディスクリプターを標準入力に差し替えることで実現できます。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
void ps();
void cat();
int pipes[2];
int main(void)
{
pipe(pipes);
if(fork() == 0)
ps();
if (fork() == 0)
cat();
wait();
}
void cat()
{
close(0);
dup(pipes[0]);
execlp("cat","cat", NULL);
}
void ps()
{
close(1);
dup(pipes[1]);
execlp("ps","ps", NULL);
}
リダイレクトとパイプに連結は、シェルによるもので、カーネルそのもの機能ではありません。そして、パイプも実ファイルもinodeとして管理されていることを考えれば、パイプもリダイレクトも、ファイルディスクリプター差し替えることで実現できるわけで、カーネルから見れば両者は同じような物と言えそうです。



