38

「Unix環境での高度なプログラミング」、第2版、W。リチャードスティーブンス著。セクション8.3フォーク機能。

説明は次のとおりです。

親と子が同じファイルオフセットを共有することが重要です。

子供をフォークし、子供が完了するのを待つプロセスを考えてみましょう。両方のプロセスが通常の処理の一部として標準出力に書き込むと想定します。親の標準出力が(おそらくシェルによって)リダイレクトされている場合、子が標準出力に書き込むときに、親のファイルオフセットが子によって更新されることが不可欠です。

私の回答:

{1}それはどういう意味ですか?たとえば、親のstd出力が「file1」にリダイレクトされる場合、子が書き込んだ後に子は何を更新する必要がありますか?親の元の標準出力オフセットまたはリダイレクトされた出力(つまりfile1)オフセット?遅くなることはできませんよね?

{2}更新はどのように行われますか?子によって明示的に、OSによって暗黙的に、ファイル記述子自体によって?フォークした後、私は親と子が独自の方法でファイル記述子の独自のコピーを持っていると思いました。では、子はどのように親側へのオフセットを更新しますか?

この場合、親が待機している間、子は標準出力に書き込むことができます。子が完了すると、親は標準出力への書き込みを続行でき、その出力は子が書き込んだものに追加されることを知っています。親と子が同じファイルオフセットを共有していなかった場合、このタイプの対話は実行がより困難になり、親による明示的なアクションが必要になります。

親と子の両方が、親に子を待機させるなど、同期の形式なしで同じ記述子に書き込む場合、それらの出力は混合されます(フォークの前に開いていた記述子であると想定)。これは可能ですが、通常の動作モードではありません。

フォークの後に記述子を処理する通常のケースは2つあります。

  1. 親は子が完了するのを待ちます。この場合、親は記述子に対して何もする必要はありません。子が終了すると、子が読み取りまたは書き込みを行った共有記述子のいずれかで、それに応じてファイルオフセットが更新されます。

  2. 親と子の両方が独自の道を進みます。ここで、フォークの後、親は必要のない記述子を閉じ、子は同じことを行います。このように、どちらも相手のオープン記述子に干渉しません。このシナリオは、ネットワークサーバーの場合によくあります。

私の反応:

{3} fork()が呼び出されると、私が理解しているのは、子が親が持っているもの(この場合はファイル記述子)のコピーを取得し、その処理を実行することだけです。親と子が共有するファイル記述子にオフセットが変更された場合、それは記述子がオフセット自体を記憶しているためにのみ発生する可能性があります。私は正しいですか?

私はその概念に少し慣れていません。

4

2 に答える 2

95

プロセスがファイルを識別するために読み取りおよび書き込み呼び出しで使用する小さな整数であるファイル記述子とカーネルの構造であるファイル記述を区別することが重要です。ファイルオフセットはファイル記述の一部です。それはカーネルに存在します。

例として、このプログラムを使用してみましょう。

#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(void)
{
    int fd;

    fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666);

    if(!fork()) {
        /* child */
        write(fd, "hello ", 6);
        _exit(0);
    } else {
        /* parent */
        int status;

        wait(&status);
        write(fd, "world\n", 6);
    }
}

(すべてのエラーチェックは省略されています)

このプログラムをコンパイルする場合は、それを呼び出してhello、次のように実行します。

./hello

これが何が起こるかです:

プログラムはファイルを開き、outputファイルがまだ存在しない場合は作成し、存在する場合はゼロサイズに切り捨てます。カーネルはファイル記述を作成し(Linuxカーネルではこれはstruct file)、呼び出し元のプロセスのファイル記述子(そのプロセスのファイル記述子テーブルでまだ使用されていない最小の非負の整数)に関連付けます。ファイル記述子が返され、プログラムに割り当てられfdます。議論のために、それが3であると仮定しますfd

プログラムはfork()を実行します。新しい子プロセスは、その親のファイル記述子テーブルのコピーを取得しますが、ファイルの説明はコピーされません。両方のプロセスのファイルテーブルのエントリ番号3は、同じを指していますstruct file

親プロセスは、子プロセスが書き込みを行う間待機します。子の書き込みにより、の前半が"hello world\n"ファイルに格納され、ファイルオフセットが6進みます。ファイルオフセットはstruct file!にあります。

子は終了し、親はwait()終了し、親はfd 3を使用して書き込みます。これは、子のによってファイルオフセットが更新されたのと同じファイル記述に関連付けられていますwrite()。したがって、メッセージの後半は最初の部分のに保存され、親のファイルオフセットがゼロの場合のように上書きされません。これは、ファイルの説明が共有されていない場合の場合です。

最後に親が終了し、カーネルはがstruct file使用されなくなったことを確認して解放します。

于 2012-07-31T06:28:30.547 に答える
5

本の同じセクションに、ファイルを開いたときに存在する3つのテーブルを示す図があります。

ユーザーfiledescriptorテーブル(プロセステーブルエントリの一部)、filetableおよびinodeテーブル(vノードテーブル)。これで、ファイル記述子(ファイル記述子テーブルへのインデックス)エントリは、iノードテーブルエントリを指すファイルテーブルエントリを指します。
これで、ファイルオフセット(次の読み取り/書き込みが発生する位置)が ファイルテーブルにあります。

つまり、親で開いているファイルがあるとしましょう。つまり、記述子、ファイルテーブルエントリ、およびiノード参照もあります。
これで、子を作成するときに、ファイル記述子テーブルが子用にコピーされます。したがって、(そのオープン記述子の)ファイルテーブルエントリの参照カウントが増加します。これは、同じファイルテーブルエントリの参照が2つあることを意味します。

この記述子は現在、親と子の両方で使用可能であり、同じファイルテーブルエントリを指しているため、オフセットを共有しています。 このような背景を持っているので、あなたの質問を見てみましょう。

  1. どういう意味ですか?たとえば、親のstd出力が「file1」にリダイレクトされる場合、子が書き込んだ後に子は何を更新する必要がありますか?親の元の標準出力オフセットまたはリダイレクトされた出力(つまりfile1)オフセット?遅くなることはできませんよね?]

子は明示的に何も更新する必要はありません。この本の著者は、
親の標準出力がファイルにリダイレクトされ、フォーク呼び出しが行われたと仮定しようとしています。その後、親は待機します。したがって、記述子が複製されます。つまり、ファイルオフセットも共有されます。これで、子が標準出力に何かを書き込むたびに、書き込まれたデータがリダイレクトされたファイルに保存されます。 オフセットは、書き込み呼び出しによって自動的に増分されます。

ここで、子が終了するとします。したがって、親は待機から抜け出し、標準の出力(リダイレクトされる)に何かを書き込みます。これで、親の書き込み呼び出しの出力が、子によって書き込まれたデータの後に配置されます->。なぜ->子が書き込んだ後にオフセットの現在の値が変更されるためです。

 Parent ( )
  {
    open a file for writing, that is get the 
    descriptor( say fd);
    close(1);//Closing stdout
    dup(fd); //Now writing to stdout  means writing to the file
    close(fd)
        //Create a child that is do a  fork call.
    ret = fork();
    if ( 0 == ret )
    {
        write(1, "Child", strlen("Child");
        exit ..
    }
        wait(); //Parent waits till child exit.

         write(1, "Parent", strlen("Parent");
    exit ..
}

Pl。上記の擬似コードを参照してください。開いたファイルに含まれる最終データはChildParentになります。したがって、子が書き込みを行ったときにファイルオフセットが変更され、offestが共有されているため、これは親の書き込み呼び出しで使用可能であったことがわかります。

2.更新はどのように行われますか?子によって明示的に、OSによって暗黙的に、ファイル記述子自体によって?フォークの後、私は親と子が独自の道を進み、ファイル記述子の独自のコピーを持っていると思いました。では、子はどのように親側へのオフセットを更新しますか?]

Now I think the answer is clear-> by the system call that is by the OS.

[3。fork()が呼び出されると、私が理解しているのは、子が親が持っているもの(この場合はファイル記述子)のコピーを取得し、その処理を実行することだけです。親と子が共有するファイル記述子にオフセットが変更された場合、それは記述子がオフセット自体を記憶しているためにのみ発生する可能性があります。私は正しいですか?]

これも明確なはずです。ユーザーファイルテーブルのエントリは、ファイルテーブルテーブルエントリ(オフセットを含む)を指します。

つまり、システムコールは記述子からオフセットをフェッチできます。

于 2012-07-31T06:55:31.503 に答える