Linux システムの下にあるものはすべてファイルです。ユーザーは、統一されたインターフェイスを通じてさまざまなドライバーを操作できます。現時点では、ファイル記述子が必要です。ファイル記述子は、ウィンドウのハンドルに似ており、読み取りや書き込みなど、ファイルに対するほとんどの操作はこの記述子を通じて実行されます。各ファイル記述子について、カーネルは 3 つのデータ構造を使用してそれを管理します。
(1) 各プロセスはプロセステーブルにレコード項目を持ち、各レコード項目はベクトルとしてみなされ、各記述子は 1 つの項目を占有します。各ファイル記述子に関連付けられているのは次のとおりです:
(a) ファイル記述子フラグ。 (現在、ファイル記述子フラグFD_CLOEXECは1つだけ定義されています)(b) ファイルテーブルエントリへのポインタ。
(2) カーネルは、開いているすべてのファイルのファイル テーブルを維持します。各ファイルテーブルエントリには以下が含まれます:
(a) ファイルステータスフラグ (読み取り、書き込み、追加書き込み、同期、非ブロッキングなど)。
(b) 現在のファイルのディスプレイスメント。 (つまり、lseek関数で演算した値)
(c) ファイルのv-nodeエントリへのポインタ。
(3) 開いている各ファイル (またはデバイス) には v ノード構造があります。 v ノードには、ファイル タイプに関するポインター情報と、ファイルに対してさまざまな操作を実行する関数が含まれています。ほとんどのファイルでは、v ノードにはファイルの i ノード (インデックス ノード) も含まれます。この情報は、ファイルを開くときにディスクからメモリに読み取られるため、ファイルに関するすべての情報をすぐに利用できます。たとえば、i ノードには、ファイルの所有者、ファイルの長さ、ファイルが存在するデバイス、ディスク上のファイルによって使用される実際のデータ ブロックへのポインタなどが含まれます。
上記のファイル システムが 3 層でカプセル化された後、各層は上から下まで異なる責任を負い、最初の層はファイルの識別に使用され、2 番目の層は管理に使用されます。独立したデータを処理し、3 番目の層はファイル システムのメタデータを管理し、ファイルを直接関連付けます。この階層化されたアイデアの利点の 1 つは、上位層が下位層の構造を再利用できることです。同じファイル テーブル エントリを指す複数のファイル記述子エントリが存在する可能性があり、同じ V ノードを指す複数のファイル テーブル エントリが存在する可能性があります。
2 つの独立したプロセスが同じファイルを開く場合、ファイルを開く各プロセスはファイル テーブル エントリを取得しますが、2 つのファイル テーブル エントリの V ノード ポインタは同じ V ノードを指します。この配置により、各プロセスは次のようになります。ファイルの独自の現在の変位を示し、さまざまなオープン方法 (O_RDONLY、O_WRONLY、ORDWR) をサポートします。
プロセスが fork を通じて子プロセスを作成すると、親プロセスと子プロセスのファイル記述子は同じファイル テーブル エントリを共有します。これは、親プロセスと子プロセスのファイル記述子が同じポイントを指すことを意味します。一般に、fork 後に必要のない fd は閉じます。たとえば、親プロセスと子プロセスがパイプまたはソケットペアを介して通信する場合、読み取り (または書き込み) の必要のない端を閉じることがよくあります。現在のファイル エントリを参照するファイル記述子がない場合にのみ、クローズ操作によって現在のファイル エントリのデータ構造が実際に破壊されます。これは、参照カウントの考え方に似ています。これは、ネットワーク プログラミングにおける close 関数と shutdown 関数の違いでもあります。前者は、ソケット ハンドルを使用している最後のプロセスが閉じられたときにのみ実際に切断されますが、後者は、何も議論せずに接続の一方の側を直接切断します。ただし、マルチスレッド環境では、父スレッドと息子スレッドがアドレス空間を共有するため、ファイル記述子は共有され、コピーは 1 つだけであるため、スレッド内で不要な fd を閉じることはできません。 fd を閉じる必要がある他のファイルも影響を受けます。親プロセスと子プロセスで開かれたファイル記述子は同じファイル テーブル エントリを共有するため、一部のシステムのサーバー プログラミングでは、プリフォーク モデルが使用されている場合 (サーバーは複数の子プロセスを事前に派生し、各子プロセスは listenfd をリッスンして受け入れます)これにより、サーバーから派生した複数のサブプロセスが各呼び出しを受け入れるため、最初のクライアント接続が到着すると、接続を取得するプロセスは 1 つだけですが、すべてのプロセスがスリープ状態になります。が覚醒し、パフォーマンスが低下します。 UNP P657 を参照。
同時に、fork 後に exec が呼び出された場合、すべてのファイル記述子は開いたままになります。これを使用して、実行後に特定のファイル記述子をプログラムに渡すことができます。同時に、ファイル記述子フラグ FD_CLOEXEC は、exec を閉じるときにファイル記述子を開いたままにするために使用されるオプションです。
dup または fcntl を介してファイル記述子を明示的にコピーすることもでき、それらは同じファイル テーブル エントリを指します。 dup2 を介してファイル記述子を指定された値にコピーします。
各プロセスにはファイル記述子テーブルがあり、プロセス間で独立しています。2つのプロセス間のファイル記述子間に直接の関係はありません。そのため、ファイル記述子はプロセス内で直接渡すことができますが、それがプロセス間で渡される場合です。その意味が失われ、Unix は sendmsg/recvmsg を通じて特殊なファイル記述子を渡すことができます (UNP セクション 15.7 を参照)。各プロセスの最初の 3 つのファイル記述子は、標準入力、標準出力、および標準エラーに対応します。ただし、プロセスがオープンできるファイル記述子の数には制限があり、オープンしているファイル記述子が多すぎると、「オープンしているファイルが多すぎる」という問題が発生します。ネットワーク サーバーでは、listenfd を介して accept が呼び出される場合、EMFILE エラーが発生します。これは主に、ファイル記述子がシステムの重要なリソースであるため、システム リソースのファイル記述子のデフォルト値が制限されているためです。単一プロセスは 1024 で、ulimit -n コマンドを使用して表示できます。もちろん、プロセス ファイル記述子の数を増やすこともできますが、これは永続的な解決策ではなく一時的な解決策です。これは、同時実行性の高いサービスを扱う場合、サーバー リソースが限られており、リソースの枯渇が避けられないためです。
lisenfd 接続をリッスンするための epoll の水平トリガー方式と組み合わせると、処理されない場合、多数のソケット接続が TCP 接続キューをフラッディングし、Listenfd は常に読み取り可能なイベントを生成し、サーバーをビジー待機状態にします。 C++ オープン ソース ネットワーク ライブラリ Muduo の作成者である Chen Shuo 氏は、EMFILE エラーが発生したときに、アイドル状態のファイル記述子を事前に準備し、まずアイドル状態のファイルを閉じ、ファイル記述子のクォータを取得してからファイルを受け入れます。その後、すぐに閉じてクライアントから正常に切断し、最後にこの状況が再び発生した場合に備えて「穴」を埋めるためにアイドル状態のファイルを再度開きます。
以上がサーバープログラミングにおけるファイル操作を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。