fopen(高水準入出力)とopen(低水準入出力)の違い

ファイルに出力する1例

int fd = open("ファイル名",O_WRONLY,S_IWRITE);

返し値はファイル・ディスクリプタです。
システムが0〜2を使っているので、openのたびに3から連番でfdに格納されます。

ちなみに

file descriptor(ファイル・ディスクリプタ)とは、UNIXシステム上で動くプログラムにて、ファイル入出力・ディスプレイ出力・ネットワーク入出力などを行うときに使用する識別子。
簡単にいうと、入力元や出力先を一意に特定するための番号です。

システムは当初、3つを自動で割り当てており、
0:標準入力(stdin)、1:標準出力(stdout)、2:標準エラー出力(stderr)
となっています。
C++やシェル・スクリプトを書く方ならば、どこかで見たことあるぞと感じたのではないでしょうか。ディスプレイやキーボードからの入出力に使う3つの番号が、最初から自動で作成されてるということになります。

プログラムは、どれかを指定することで、ユーザと対話ができることになります。たとえば、printfで文字列を出力するときは、システムが内部で「stdoutに出力」(vfprintf (stdout, format, arg);)という処理を行なっています。


そしてfopenも使えます。

FILE* fp = fopen("ファイル名","w");

返し値はファイルポインタです。


ファイルをオープンするときは通常、高水準入出力(fopen)を利用するかと思いますが、openは低水準入出力。

fopen内部でopen関数が呼び出されています。

fopenとopenの違いは、ざっくりいうとバッファの有無です。

fopenはメモリ上に入出力のためのバッファ領域を設け、そこを介して入出力を行います。
たとえばfputsで文字列を出力したとき、直接ハードディスクに書き込まず、一旦メモリ上のバッファに貯めます。そしてバッファが一杯になってから、ハードディスクに書きこみます。

どうしてこんな事をするのか?は、そうしたほうが高速な出力が可能だからです。
たとえば、数バイト出力するfputsが100回続いていたとしたら、バッファがなければ100回ハードディスクにアクセスする必要があります。そのたびにHDDのディスクの円盤をぐるぐる回して書き込む場所を特定するので、低速になってしまうのです。

しかし(fopenを使い)バッファにデータを貯めこんで、1回でHDDに書き込むことができるならば、書き込む場所を特定するのは1度で済みます。書きこみが高速化できます。

もちろん、open(バッファなし)にも利点があり、リアルタイムでの出力が可能になるということがあります。

1つのファイルに、違うプロセスやスレッドが書きこみを行う場合、データが時系列順になっていないことがあります。

これは、fopenやfputsでアクセスすると一旦バッファに置いて満杯になるまで出力しないためで、プログラムで出力した順番ではなく、バッファが一杯になった順番でHDDに書きこみが始まるためです。

もしデバッグのためにログを出力しているならば、この順不同なログは致命的で、まともなログ追いは難しくなってしまいます。

ちなみにfopenでも直後に
setvbuf(fp , NULL, _IONBF, 0);
としてやれば、バッファリングなしでの入出力が可能です。