3

fclose()子プロセスが終了するまで、ファイル記述子ブロックを ing した後、ここで呼び出しますdup()(おそらくストリームが終了したため)。

FILE *f = popen("./output", "r");
int d = dup(fileno(f));
fclose(f);

ただし、のpipe()fork()、を手動で実行してから、パイプの読み取りファイル記述子を ing すると、元のファイルを閉じてもブロックされません。execvp()popen()dup()

int p[2];
pipe(p);
switch (fork()) {
    case 0: {
        char *argv[] = {"./output", NULL};
        close(p[0]);
        dup2(p[1], 1);
        execvp(*argv, argv);
    }
    default: {
        close(p[1]);
        int d = dup(p[0]);
        close(p[0]);
    }
}

なぜこれが発生するのですか? また、FILE *返された fromを閉じてpopen()、代わりにファイル記述子を使用するにはどうすればよいですか?

アップデート:

ドキュメントには を使用するように記載されていますがpclose()fclose()ブロックも同様です。さらに、私は glibc コードをいじって、 をpclose()呼び出すだけfclose()です。fclose()またはを使用しても、動作は同じpclose()です。

4

3 に答える 3

9

http://linux.die.net/man/3/popen

pclose() 関数は、関連付けられたプロセスが終了するのを待ち、wait4() によって返されるコマンドの終了ステータスを返します。

pclose() は終了ステータスを返す必要があるため、子プロセスが終了して生成されるまで待機する必要があります。fclose() は pclose() を呼び出すため、同じことが fclose() にも当てはまります。

fork と exec を実行し、残りを自分で行う場合、(直接的または間接的に) pclose() を呼び出すことはないため、終了時に待機する必要はありません。ただし、プログラムが SIGCHLD を無視するように設定されていない限り、子プロセスが終了するまでプロセスは終了しない (代わりにゾンビになる) ことに注意してください。しかし、少なくともあなたのコストは最初に終了するために実行されます.

于 2009-11-16T04:50:29.230 に答える
8

これまでの回答の一般性(RTFM、tyvmが可能)に失望したので glibcのソースをステップスルーして読むことにより、これを徹底的に調査しまし

glibcでは、追加の効果なしpclose()に直接呼び出すfclose()ため、2つの呼び出しは同じです。実際、あなたは交換可能pclose()に使用することができます。fclose()これは進化した実装の純粋な偶然であると確信しており、から戻ってきpclose()たものを閉じるためにを使用することをお勧めします。FILE *popen()

魔法はにありpopen()ます。FILE *glibcのsには、、、、および関連性のある呼び出しを処理するための適切な関数へのポインタを含むジャンプテーブルが含まれてfseek()fread()ますfclose()。を呼び出すときpopen()に、によって使用されるものとは異なるジャンプテーブルが使用されfopen()ます。closeこのジャンプテーブルのメンバーは、が指す領域に格納されているpidを呼び出す特殊関数を指し_IO_new_proc_closeます。waitpid()FILE *

これが私のバージョンのglibcの関連するコールスタックであり、何が起こっているかについてのメモで注釈を付けています。

// linux waitpid system call interface
#0  0x00f9a422 in __kernel_vsyscall ()
#1  0x00c38513 in __waitpid_nocancel () from /lib/tls/i686/cmov/libc.so.6

// removes fp from a chain of proc files
// and waits for the process of the stored pid to terminate
#2  0x00bff248 in _IO_new_proc_close (fp=0x804b008) at iopopen.c:357

// flushes the stream and calls close in its jump table
#3  0x00c09ff3 in _IO_new_file_close_it (fp=0x804b008) at fileops.c:175

// destroys the FILEs buffers
#4  0x00bfd548 in _IO_new_fclose (fp=0x804b008) at iofclose.c:62

// calls fclose
#5  0x00c017fd in __new_pclose (fp=0x804b008) at pclose.c:43

// calls pclose
#6  0x08048788 in main () at opener.c:34

つまり、を使用すると、ファイル記述子であっても、子プロセスが終了するまでブロックされるため、popen()返されたファイルFILE *を閉じてはなりません。dup()もちろん、この後、パイプへのファイル記述子が残ります。この記述子には、終了する前に子プロセスが書き込み()したものがすべて含まれます。

fread()から返されたファイルポインタを使用しないことでpopen()、基になるパイプが変更されることはありません。でファイル記述子を使用しfileno()、で終了するのが安全pclose()です。

于 2009-11-20T10:44:37.790 に答える