web-dev-qa-db-ja.com

誰でもfork()の後に「ファイル記述子」に関する簡単な説明を説明できますか?

「Unix環境での高度なプログラミング」、第2版、W。リチャードスティーブンス著。

セクション8.3フォーク機能。

説明は次のとおりです。

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

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

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

[2。更新はどのように行われますか?子によって明示的に、OSによって暗黙的に、ファイル記述子自体によって行われますか?分岐後、親と子は独自の方法で行き、ファイル記述子のCOPYを所有します。子は親側へのオフセットをどのように更新しますか?]

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

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

分岐後の記述子の処理には、通常2つのケースがあります。

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

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

[3。fork()が呼び出されたとき、私が理解しているのは、子が親が持っているもの、この場合はファイル記述子のCOPYを取得し、そのことを行うことです。親と子が共有するファイル記述子のオフセットの変更は、記述子がオフセット自体を記憶しているためにのみ可能です。]

申し訳ありませんが、このコンセプトは初めてです。

何か助け?ありがとう。

33
user1559625

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

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

_#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に割り当てられます。引数のために、fdが3であると仮定します。

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

子プロセスが書き込む間、親プロセスは待機します。子の書き込みにより、_"hello world\n"_の前半がファイルに保存され、ファイルオフセットが6ずつ進められます。ファイルオフセットは_struct file_!にあります!

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

最後に、親が終了し、カーネルは_struct file_が使用されていないことを認識し、解放します。

79
Alan Curry

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

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

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

この記述子は親と子の両方で使用できるようになり、同じファイルテーブルエントリをポイントするため、オフセットを共有します。この背景を使用して質問を確認できます。

  1. どういう意味ですか?たとえば、親のstd出力が「file1」にリダイレクトされる場合、子の書き込み後に子は何を更新する必要がありますか?親の元の標準出力オフセットまたはリダイレクトされたouput(i.e 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()が呼び出されたとき、私が理解しているのは、子が親が持っているもののコピーを取得し、この場合はファイル記述子を取得し、そのことを行うことです。親と子が共有するファイル記述子のオフセットが変更された場合、それは記述子がオフセット自体を記憶しているためにのみ可能です。私は正しいですか?]

これも明確にする必要があります。ユーザーファイルテーブルのエントリは、ファイルテーブルテーブルエントリ(オフセットを含む)を指します。

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