OpeningPipeOnWindows

2007-07-12 20:06:20 +0900 (546d); rev 2

UNIX に慣れていると、fork のない Windows では どうやってパイプをつないでいるのか疑問に感じる。 ので調べてみた。

パイプをつなぐ

まず、Windows に fork はないが、子プロセスを作ることはできる。 この場合、新しく実行ファイルを指定してプロセスを作るわけだ。 (子)プロセスを生成するには ?CreateProcess を使う。

?CreateProcess ではファイルハンドル (ファイルディスクリプタのようなもの) を継承するかどうかを引数で指定する。 この引数を使ってファイルハンドルを継承させれば、 パイプを受け渡すことができる。

また、パイプ(無名パイプ)を作るには CreatePipe を使う。

Ruby では rb_w32_pipe_exec (win32/win32.c) でパイプをつないでいるので、 そのコードを見てみよう。

    SECURITY_ATTRIBUTES sa;

    // 中略

    sa.nLength              = sizeof (SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle       = TRUE;    // ファイルハンドルを継承。このフラグが最終的にCreateProcessに渡される。
    ret = -1;

    // 中略

        else if (reading) {
            if (!CreatePipe(&hIn, &hOut, &sa, 2048L)) {    // パイプ作る
                errno = map_errno(GetLastError());
                break;
            }

    // 中略

        /* create child process */
        child = CreateChild(cmd, prog, &sa, hIn, hOut, NULL);   // 子プロセス作る
        if (!child) {
            CloseHandle(hOrg);
            CloseHandle(hDupFile);
            break;
        }

    // 以下略

static struct ChildRecord *
CreateChild(const char *cmd, const char *prog, SECURITY_ATTRIBUTES *psa,
            HANDLE hInput, HANDLE hOutput, HANDLE hError)
{
    // 略

    RUBY_CRITICAL({
        fRet = CreateProcess(shell, (char *)cmd, psa, psa,
                             psa->bInheritHandle, dwCreationFlags, NULL, NULL,    // 継承しろ!
                             &aStartupInfo, &aProcessInformation);
        errno = map_errno(GetLastError());
    });

第五引数 (この場合は SECURITY_ATTRIBUTES 構造体の bInheritHandle メンバー) を TRUE にしておくのが重要。 この引数を TRUE にするとファイルハンドルが継承されて、パイプをつなぐことができる。

なお、パイプを stdout につなぐには、GetStdHandleSetStdHandle を使う。

see also: http://msdn2.microsoft.com/en-us/library/aa365780.aspx


system revision 1.162