ぷるぷるの雑記

低レイヤーがんばるぞいなブログ. 記事のご利用は自己責任で.

ファイル記述子とdupシステムコールについてのメモ

最近Linuxシステムコールについて勉強しているのですが、とても面白いです。ネットでも情報は手に入りますが、やはり本で読みたいので『Lunuxシステムコール基本リファレンス OSを知る突破口』という本を買いました。

その中でもファイル記述子を複製するdupシステムコールの理解に苦労したので、自分なりのメモを残しておきます。

dupシステムコールとは

以下、JM Projectからの引用です。

dup() システムコールは、 ファイルディスクリプター oldfd のコピーを作成し、 最も小さい番号の未使用のファイルディスクリプターを 新しいディスクリプターとして使用する。 成功が返された場合には、 古いファイルディスクリプターと新しいファイルディスクリプターは 互いに可換なものとして使うことができる。 2つのファイルディスクリプターは同じファイル記述 (description) (open(2) 参照) を参照しており、したがってファイルオフセットやファイル状態フラグが 共有される。例えば、一方のファイルディスクリプターに対して lseek(2) を使ってファイルオフセットを変更した場合、もう一方のファイルディスクリプターの オフセットも変化する。

これだけだとよくわかりませんね。特に ファイルディスクリプタファイル記述という言葉が引っ掛かります。

ファイルディスクリプター(ファイル記述子)とは、C言語のアプリケーション上でファイルを管理するために(OSによって)割り振られた0以上の整数の番号です。通常C言語でファイルにアクセスするにはファイル構造体へのポインタを用いると思いますが、このファイル構造体の中にもファイルディスクリプター(実体はint型の変数)が含まれています。0は標準入力だとか言いますが、これは0番のファイル記述子は標準入力(キーボード)を表しているということになります。

ファイル記述という言葉は検索してもあまりヒットしなかったのですが、この説明から察するにファイル記述子によって指定されるファイルそのものを表すようです。正確には、ファイルのみならずカーソルの位置などの情報も含んでいるようですね。

ファイルディスクリプターは0から順番につけられるので、そのプロセスにおいて何番目にオープンされたファイルなのかということも表しています。ただし、ファイルディスクリプタのうち0、1、2はそれぞれ標準入力、標準出力、標準エラー出力としてあらかじめオープンされています。

ファイルディスクリプタ 用途
0 標準入力(キーボードからの入力)
1 標準出力(プロセスを起動したコンソール)
2 標準エラー出力(プロセスを起動したコンソール)

つまり、C言語のアプリケーションでfopen()等のopenシステムコールを利用する関数を呼び出すと、ファイルディスクリプタ3からオープンするということですね。

話をdupシステムコールに戻すと、このシステムコールによってファイルディスクリプタを複製することが出来ます。以下のようなコードがあったとしましょう。

int fd, new_fd;
fd = open("hoge.txt", O_RDONLY);
if( new_fd = dup(fd)){
    printf("fd = %d\n",fd);
    printf("new_fd = %d\n",new_fd);
}

// 実行結果
// fd = 3
// new_fd = 4

この場合、ファイルディスクリプタ4は3のコピーであり、どちらもhoge.txtへの参照になっています。この結果から推測できるように、dup()はオープンされていないファイルディスクリプタのうち最小のものを割り当てます。したがって、プロセス起動時にすでにオープンされているファイルディスクリプタ0、1、2の参照先をほかのファイルディスクリプタの複製先にしたい場合、dupシステムコールの前にcloseシステムコールによってクローズさせておく必要があります。

標準出力をファイルにリダイレクトするアプリケーションが例として挙げられることが多いですが、close(1)が必要なのはこのためだったんですね。

// よくあるdupシステムコールのサンプルコード
int fd;
fd = open("hoge.txt", O_RDONLY); // 1      
close(1); // 2     
if( dup(fd) ){  // 3                                              
    printf("This is a redirect message");
}

さて、このサンプルコードを図式すると次のような感じでしょうか。

f:id:prupru_prune:20220312081602p:plain
dupのサンプルコードにおけるファイルディスクリプタの図式

参考

dev.grapecity.co.jp