【Linux】ファイルディスクリプタとは何か【stream】

| 0件のコメント

httpサーバが複数のクライアントを識別できる背景にはファイルディスクリプタの存在があります。仮にファイルディスクリプタが存在しないと, 誰にレスポンスを返して良いかわからなくなってしまいます。

今回はファイルディスクリプタの基礎について。

Streamとは何か

ファイルディスクリプタを理解するには Stream[1] が何かを知る必要があります。

下図のようにプロセスからファイルにアクセスするときに, 通り道となるのが Stream です。
バイト列が流れることから byte-stream とも呼ばれます。
逆に言えば、バイトをやり取りするなら何でも Stream で扱えます。

stream

プロセスからプロセスに繋ぐときには pipe[2]を使いますが、この時にバイト列を結びつけるのも Stream です。
Stream を制御するのはカーネルの役割です。
open(2), read(2), write(2)などのシステームコールで Stream をカーネルに出してもらいます。

ファイルディスクリプタとは何か

ファイルディスクリプタ (以下, fd)は実は単なるint型の整数です。
例えば /proc/PID/fd を覗くと数字の列がありますが, これはプロセスで使用しているfdの一覧です。

何故 fd が必要かというと, 前述したように Stream はカーネルが管理しています。つまり, ユーザ空間から直接はアクセスできない領域になります。
そこで, fd の番号によって Sstream と結びつくことによってプロセスから制御することができるのです

有名な fd は標準入力(stdin, 0), 標準出力(stdout, 1), 標準エラー(stderr, 2)です。
stdioライブラリで Stream を制御するにはFILE型を用います。

FILE *fp;

FILE構造体は色々と型がありますがfdとバッファを持っています。
つまり, FILEは fd にバッファを持たせたラッパー機能です。

typedef struct {
 char mode;
 char *ptr;
 int rcount;
 int wcount;
 char *base;
 unsigned bufsiz;
 int fd;
 char smallbuf[1];
} FILE;

socket通信と呼ばれるのも, Streamを繋ぐことで成り立っています。

ネットワーク用語のパケットも Stream をある長さで切ったバイト列と言い換えることができます。
例えば, NIC に対してクライアントからパケットが流れこんできますが, カーネルが検出しドライバがネットワーク/トランスポート層の処理を担当しアプリ側でsocketを使って誰からの通信で誰に返すかというのを制御 (実行はカーネルランド)しています。

ファイルディスクリプタとファイルシステム

Linuxカーネルがサポートしているファイルシステムは ext4, jffs など様々ありますが, fopen(3)で Stream を開く場合はファイルシステムは指定はしません。
これは, LinuxカーネルのVFS (virtual File System)が抽象化しているおかげです。

fopen(3)が実行された時の流れを簡単に追ってみます。

FILE *fp = fopen("/path/to/your/file", "r"))

プロセス内でFILE構造体へのポインタを宣言するとVFSが実際のファイルと対応付けを行います。
VFSはfopen(3)の命令を受けてI/Oスケジューラ (ブロックレイヤ)に登録し, i-node番号を用いてブロックデバイスにアクセスします。

VFSは内部にソフトウェアキャッシュを持っており, /proc/meminfo の Cached で確認できます。
メモリをあまり使用していないのにメモリ使用率が高く見える原因はこのためです。

また, Kernelはアクセスが低速になるブロックデバイスにユーザ空間のプロセスが引きずられないようになっています。
具体的には, 例えばread(2)はすぐに実行しますが, write(2)は先に戻り値だけ返して実際の書き込みはすぐには行わないことで見かけ上の応答性を高めています。


[1] streamは「小川」つまり細い流れという意味があります。
[2] 通常の pipe以外にも, 拡張された named pipe (FIFO)は mkfifo(3)で作成できファイルのように扱うことができます。

コメントを残す

必須欄は * がついています