メインコンテンツへスキップ
  1. ノート/
  2. Linux/

Linuxのファイルディスクリプタ(fd)

·1804 文字·4 分· loading · loading · · ·
ICE345
著者
ICE345
CS Student | System | Linux | OCaml

1. よく使うリダイレクトから始める
#

Linuxでよく見かける次のコマンドを例にします。

echo log > /dev/null 2>&1

それぞれの記号には次の意味があります。

  • >:標準出力を指定した場所へリダイレクトする。
  • /dev/null:書き込まれたデータを捨てる特殊なデバイスファイル。
  • 1:標準出力(stdout)。
  • 2:標準エラー出力(stderr)。
  • &:数字をファイル名ではなくファイルディスクリプタとして扱うことを表す。

したがって、echo log > /dev/nullは標準出力を/dev/nullへ送り、画面には表示しません。その後の2>&1は、標準エラー出力(FD 2)を標準出力(FD 1)と同じ宛先へ向けます。FD 1がすでに/dev/nullへ向いているため、エラー出力も/dev/nullへ捨てられます。

2. ファイルディスクリプタとは
#

Linuxでは、通常のファイル、ディレクトリ、リンク、デバイス、パイプ、ソケットなどを、ファイルに似たインターフェースで扱います。ファイルディスクリプタ(file descriptor、FD)は、プロセスがオープンした対象を参照するための非負整数です。

プロセスの標準FDは、起動時から次のように割り当てられます。

FD名前役割
0stdinキーボードやパイプからの入力
1stdout通常の出力
2stderrエラーや診断メッセージ

FD 0、1、2を使っている状態で新しいファイルをopen()すると、通常はFD 3が返されます。

3. カーネル内部の3つの表
#

簡略化すると、FDは次のような配列の添字です。

プロセスのファイルディスクリプタ表
    FD 0 ─┐
    FD 1 ─┼→ オープンファイル表のエントリー
    FD 2 ─┘             ↓
                    inodeとファイルシステム

実際には、Linuxカーネルは主に次の構造を分けて管理します。

  1. プロセスごとのFD表:FD番号とオープンファイルへの参照。
  2. システム全体のオープンファイル表:ファイルオフセット、オープンフラグ、参照カウントなど。
  3. ファイルシステムのinode表:ファイルのメタデータとデータブロックの位置。

同じstruct fileを複数のFDが参照する場合、現在位置などの状態も共有されます。

4. リダイレクトの仕組み
#

4.1 標準出力をファイルへ送る
#

command > output.log

シェルは、commandを実行する前に、標準出力FD 1をoutput.logへ向けます。commandから見ると、通常どおりFD 1へ書き込んでいるだけです。

追記したい場合は>>を使います。

command >> output.log

4.2 標準エラー出力だけを保存する
#

command 2> error.log

4.3 標準出力と標準エラー出力を別々に保存する
#

command > output.log 2> error.log

4.4 両方を同じファイルへ送る
#

command > all.log 2>&1

順番が重要です。2>&1は「FD 2を、現在のFD 1と同じ宛先へ向ける」という意味です。

command 2>&1 > all.log

この書き方では、最初にFD 2がその時点のFD 1へコピーされ、その後FD 1だけがall.logへ向きます。そのため、stderrはファイルへ入らず、端末に残ることがあります。

5. dup()とFDの共有
#

dup()dup2()dup3()は、既存のFDを複製するシステムコールです。シェルはコマンドの標準入出力を組み替えるとき、内部で同じようなFD操作を利用します。

int fd = open("output.log", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
printf("この出力はoutput.logへ書き込まれる\\n");

dup2(fd, 1)を実行すると、FD 1がfdと同じオープンファイルを参照します。以後、printf()write(1, ...)の出力は、そのファイルへ送られます。

6. パイプとFD
#

パイプは、一方のプロセスの標準出力を別のプロセスの標準入力へ接続するためにも使われます。

printf '%s\\n' *.log | grep ERROR

シェルは、左側のコマンドのFD 1をパイプの書き込み端へ、右側のコマンドのFD 0をパイプの読み込み端へ接続します。各プログラムは「標準出力へ書く」「標準入力から読む」だけで、相手の具体的な実装を知る必要がありません。

まとめ
#

  • FD 0、1、2は、それぞれstdin、stdout、stderrに対応する。
  • >は標準出力、2>は標準エラー出力をリダイレクトする。
  • 2>&1は、FD 2を現在のFD 1と同じ宛先へ向ける。
  • リダイレクトは、コマンドを起動する前にシェルがFDを組み替えて実現する。
  • dup()fork()では、複数のFDが同じオープンファイル状態を共有することがある。

评论