ファイルのオープン、クローズ

このエントリーをはてなブックマークに追加

前回は標準入出力に付いて説明しましたが、今回からはUnix系OSにとって標準入出力とともに重要なファイルに付いて説明していきます。標準入出力の時はC言語の標準関数から説明しましたが、今回はシステム関数の解説が中心です。
さて、このページではファイル操作の為に必要なオープン、クローズ処理について解説していきます。

Unixではすべてがファイル

プログラムに入る前にそもそもファイルがなぜUnix系OSで大切かというということを少し説明しましょう。
Unix系OSでは基本的にすべてがファイルとして表現されています。例えばHDD、CD/DVD/BlueRay、FDDと一般的な物から普段は使わないRS-232Cのような通信もファイルです。また、ターミナルや読み込むと永遠に"0"が読めるデバイス、ランダムな数字を読めるデバイスといった変り種もあります。

ただし、完全にありとあらゆる物がファイルになっているわけではありません。例えば、Linuxではイーサネット(いわゆるLAN)はファイルでは無かったり、OSによって多少の違いはありますが、完璧ではありません。ちなみに、この欠点を修正する事を目標としたUNIXの後継OSとしてPlan9というのがあります。あまり普及はしてませんが...

ともかく詳しくは後ほど説明しますが、このようにすべてファイルにすることによりプログラマは操作対象先の変更だけで、あとはファイル操作を行う関数を呼び出せば簡単にプログラムがかけるようになっています。
また、TCPやUDPのような通信関係もファイル操作の延長としてプログラムが可能です。つまり、ファイル操作こそがUnix系OSでの基礎であり奥義でもあるのです。


ファイルのオープンとクローズ

標準入出力はデフォルトでオープンされている状態だったので説明が飛びましたが、ファイルの場合は明示的にオープン・クローズを行う必要があります。
ここではまずもっとも基本的なopen(2)、close(2)の説明をします。


ファイルのオープン

ファイルのオープンにはopen(2)を使用します。まずはプロトタイプから。対比の為にfopen(3)も同時に示します。

//fopen
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);

//open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);


fopen(3)ではmodeがあります。これは書き込み専用、読み込み専用などを指定する物でしたが、open(2)ではこれに当たるのがflagsです。flagsはfopen(3)よりも細かい指定ができます。
flagsに指定できる値のうち良く使う物をあげておきます。

  • O_RDONLY : 読み込み専用
  • O_WRONLY : 書き込み専用
  • O_RDWR : 読み書き両用
  • O_APPEND : 追記モード
  • O_CREAT : ファイルが存在しない場合作成
  • O_TRUNC : ファイルを0に切り詰める

flagsは指定は次のようにorで複数指定ができます。
//create(2)と同じ動作
open(filename,  O_CREAT|O_WRONLY|O_TRUNC);

勘が良ければflagsを複数指定する事でfopen(3)が作れられていることが想像出来るでしょう。
例えば、"w"と"w+"はこのようになるでしょう。
//fopen(filename, "w")
open(filename, O_CREAT|O_WRONLY|O_TRUNC)

//fopen(filename, "w+")
open(filename, O_CREAT|O_RDWR|O_TRUNC)


ファイルのクローズ

次はクローズです。同じくプロトタイプからです。こちらはopen(2)とは違い非常に単純です。特に説明することは無いと思います。

#include <unistd.h>
int close(int fd);


サンプルコード

では実際にサンプルコードを見てみましょう。

//open_close.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
	int fd;

	fd = open("test.txt", O_CREAT|O_WRONLY|O_TRUNC);
	if (fd == -1) {
		fprintf(stderr, "Can not open file:%s\n", "test.txt");
	}
	printf("Open OK:%d\n", fd);

	close(fd);

	return 0;
}


サンプルコード解説

では、一つ一つ説明していきます。
まずはファイルディスクリプタを宣言します。型はintです。

	int fd;


次にファイル名を指定してopen(2)を行います。今回は、ファイルを0バイトで読み書き両用でオープンします。

	fd = open("test.txt", O_CREAT|O_WRONLY|O_TRUNC);


関数を呼んだら当然エラー処理が必要です。基本的にシステム関数に戻り値がある場合は負数はすべてエラーとなります。デフォルトのエラーは"-1"で、エラーに複数の意味を持たせたい倍は"-1"、"-2"、...というように負数がいくつかあります。ただし、"-1"以外の負数は関数によって違う場合があります。詳しくはmanを見てください。
単純にエラーチェックするだけならば負数かどうかのチェックだけでも良いでしょう。

	if (fd == -1) {
		fprintf(stderr, "Can not open file:%s\n", "test.txt");
	}


ここでファイルディスクリプタを出力します。標準入出力は常に決まっていましたが、それ以外はOS側が自動的に値を決めます。通常は3以降の値を順番に付けていくことになります。つまり、今回は"3"と表示されるはずです。ただし絶対では無いので注意してください。

	printf("Open OK:%d\n", fd);


最後はクローズです。fclose(3)と同じ注意点としてクローズ後のファイルディスクリプタは使用しないでください。

	close(fd);


それでは実行してみます。
"ls"すると実際にファイルが作られている事が分かります。ただし"ls -l"すると権限がおかしな事になっているのに気づくはずです。

$ gcc -o open_close open_close.c
$ ./open_close
Open OK:3
$ ls -l test.txt
--wx--S--- 1 khondalit khondalit 0 2011-10-17 22:07 test.txt


サンプルコード解説

この権限の問題を解決するにはmodeを指定する必要があります。次の2行は同じ意味です。

	fd = open("test.txt", O_CREAT|O_WRONLY|O_TRUNC, 0755);
	fd = open("test.txt", O_CREAT|O_WRONLY|O_TRUNC,
	                      S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);

上記のように書き換えて実行すると以下のようになります。セキュリティの為にも権限はできるだけ付けるように習慣付けましょう。
$ gcc -o open_close open_close.c
$ ./open_close
Open OK:3
$ ls -l test.txt
-rwxr-xr-x 1 khondalit khondalit 0 2011-10-17 22:38 test.txt


まとめ

今回作成したプログラムの動作は空のファイルを作る動作です。これは"touch"コマンドと良く似た動作です。このプログラムに引数処理だけ行えば"touch"とほぼ同じ処理となります。(実際には時間処理等も行う必要がありますが...)
引数処理は別途説明しますが、これでファイル操作の為に必要なopen(2)、close(2)の説明が終わりました。次回はさらに標準入出力 2で使用したread(2)、write(2)関数を使っていきます。


かなりの良本です。ファイル操作を始めとして非常に役に立つ本です。こういった基礎知識のために本が必要な方はこれをおすすめします。