システムプログラミング 第7回、8回 ファイルシステム関連の システムコール 情報工学科 篠埜 功 ディレクトリファイル ディレクトリファイルには、ファイル名からiノード 番号(iノードはindex nodeの略)への対応がファ イルの数だけ格納されている。(実際のデータ 構造は、ハッシュテーブルや線形リストなど。) ファイルのiノード番号は $ ls –li 等、lsに-iオプションを与えると確認できる。 ファイルシステムの実装 ファイルシステムは、ブートブロック、スーパーブロック、 iノードリスト、データブロックの4つの領域から構成され る。 • ブートブロック --- OSを起動するためのプログラムを 格納。 • スーパーブロック --- ファイルシステムの大きさ、ブ ロック数、ブロックサイズ、空きブロック、iノードリストの 大きさ、空きiノードリストの情報などを格納。 • iノードリスト --- iノードのリスト。iノードには各ファイル の種類、所有者、permission、変更時刻、ファイルサイ ズ、データブロック内の場所などが格納されている。 • データブロック --- ファイルの中身が格納されている。 openシステムコール ファイルからのデータの読み込み、ファイルへのデー タの書き込みをするには、まずファイルをオープンす る。これを行うのがopenシステムコールである。 openシステムコールを使う場合、types.h, stat.h, fcntl.hをインクルードする。 openシステムコールの引数 openシステムコールは、パス名、フラグの2引数ある いは、これらにモードを加えた3引数で呼び出す。 パス名で指定されたファイルを、フラグに従ってオープ ンし、ファイル記述子(file descriptor, int型)を返す。フ ラグがO_CREATの場合、モードが必要。 ファイル記述子は、利用者ファイル記述子表の 何番目かを表す。 オープンするとは、データ入出力用のバッファを確保し、 利用者ファイル記述子表中の1つの構造体を割り当て、 構造体の各メンバーに初期値を設定することをいう。 利用者ファイル記述子表についてはdupシステムコー ルの説明時に説明する。 openシステムコールの代表的なフラグ O_RDONLY --- 読みだしのみ O_WRONLY --- 書き込みのみ O_RDWR --- 読み出し、書き込みの両方を行う O_CREAT --- ファイルが存在しない場合作成する。第3引 数のモードでファイルのpermission等を設定する。 これらのフラグはfcntl.hに記述されているので、インク ルードして使う。 モード openシステムコールの第3引数に与えられるモー ドでは、ファイルのpermissionおよびセットユーザID ビット、セットグループIDビット、stickyビット(/tmpな どで使用)を12桁の2進数で表す。代表的な数値 はstat.hでマクロとして提供されているが、数値で 直接指定してよい。 (例)S_IRWXU --- 所有者はread, write, executeが できる。 その他にもあるが、 $ man –S 2 open で確認。 readシステムコール ファイルからデータを読み出すためのシステムコー ルがreadシステムコールである。 unistd.hをインクルードする。 ファイル記述子、データ格納領域へのポインタ、読み 出しバイト数を引数に与える。返り値は読み出したバ イト数。これが0のときはファイルの最後まで読み終 わっているということになる。 readシステムコールが正常終了しなかった場合は-1 が返ってくる。このときはperrorでエラーメッセージを 表示する。 writeシステムコール ファイルへデータを書き込むためのシステムコール がwriteシステムコールである。 unistd.hをインクルードする。 ファイル記述子、書き込むデータが格納されている 領域へのポインタ、書き込みバイト数を引数に与える。 返り値は書きこんだバイト数。 writeシステムコールが正常終了しなかった場合は-1 が返ってくる。このときはperrorでエラーメッセージを 表示する。 closeシステムコール closeシステムは、ファイル記述子を引数に受け取 り、そのファイルをクローズする。 クローズするとは、入出力用バッファを解放し、利 用者記述子表内の構造体を解放することをいう。 (同時に開けるファイル数に制限があるので、閉じ るのがよい。閉じなければプロセス終了時に閉じ られる。) 例1:テキストファイルの先頭にaを書き込む #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main (void) { int fd, n; char c = 'a'; if ((fd = open ("test", O_WRONLY)) == -1) { perror ("open"); exit(1); } /* 続き */ if (write (fd, &c, 1) != 1){ perror ("write"); exit(1); } if (close (fd) == -1) { perror ("close"); exit(1); } return 0; } 演習課題2 • テキストファイル(ファイル名はtestなど)の先 頭文字を読み取り、その文字を2文字目に書 きこむ。Openシステムコールの第2引数(フラ グ)はO_RDWRにする。 read, writeシステムコールを呼ぶたびに、読 み書きのためのポインタ(kernel内部のポイン タ)が1つ進むので、readで読み取ったあとに writeで書き込めば、2文字目に書きこまれる ことになる。 例2 /* テキストファイル全部表示 */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> int main (int argc, char * argv []) { int fd, n; char c; if (argc!=2) { fprintf (stderr, "Usage: %s filename\n", argv[0]); exit(1); } /* 続き */ if ((fd = open (argv[1], O_RDONLY)) == -1) { perror ("open"); exit(1); } while ((n = read (fd, &c, 1) ) > 0) printf ("%c", c); if (n==-1) { perror ("read"); exit(1); } if (close (fd) == -1) { perror ("close"); exit(1); } return 0; } 複数バイトずつ読み込む readシステムコールで、複数バイト単位で読み込む こともできる。 readシステムコールの返り値は、 (1) 正の場合、読み込んだバイト数を表す。 (2) 0 の場合、ファイルの内容を既に全部読み終 わっていたことを表す。 (3) -1の場合、システムコールが何らかの理由で正 常終了しなかったことを表す。この場合はライブラリ 関数perrorでエラー内容を表示するべき。 例えば、ファイルサイズ260バイトのファイルを100バ イト単位で読み込むと、最後の回は60バイト(返り値 も60)になり、その後は返り値は0となる。 例3 /* テキストファイル全部表示 */ #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main (int argc, char * argv []) { int fd, n; char c[100]; if (argc!=2) { fprintf (stderr, "Usage: %s filename\n", argv[0]); exit(1); } /* 続き */ if ((fd = open (argv[1], O_RDONLY)) == -1) { perror ("open"); exit(1); } while ((n = read (fd, c, 100) ) > 0) if (write (1, c, n) != n) { perror ("write"); 1は標準出力 exit(1); }; if (n == -1) { perror ("read"); exit(1); } if (close (fd) == -1) { perror ("close"); exit(1); } return 0; } 例4 /* 標準入力を標準出力へ書きだ す*/ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> int main (int argc, char * argv []) { int fd, n; char c[100]; while ((n = read (0, c, 100) ) > 0) if (write (1, c, n) != n) { perror ("write"); exit(1); }; /* 続き */ if (n == -1) { perror ("read"); exit(1); } return 0; } 0は標準入力 1は標準出力 新しいファイルの作成 新しいファイルの作成は、openシステムコールの 第2引数のフラグにO_CREATを指定する。(ファイ ルが存在していたらそのファイルを使う。存在しな ければファイルを新しく作る。) 他のフラグと組み合わせて指定できる。組み合わ せるときはビットのor演算子|を用いる。 例えば、write onlyで開きたい場合は、openシス テムコールの第2引数に O_WRONLY | O_CREAT を指定する。さらに、存在しているときに内容を消 したいときは O_TRUNCをさらに追加で指定する。 例5 /* 入力をファイルへ書きだす*/ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> int main (int argc, char * argv []) { int fd, n; char c[100]; if (argc!=2) { fprintf (stderr, "Usage: %s filename\n", argv[0]); exit(1); } /* 続き */ if ((fd = open (argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) { perror ("open"); 0は標準入力 exit(1); } while ((n = read (0, c, 100) ) > 0) if (write (fd, c, n) != n) { perror ("write"); exit(1); }; if (n == -1) { perror ("read"); exit(1); } if (close (fd) == -1) { perror ("close"); exit(1); } return 0; } 演習課題3 実行ファイルの引数に2つのファイル名を受けとり、 1つ目のファイル(存在するファイル)のコピーを2つ 目のファイルに作成するプログラムを作成せよ。(コ ピーコマンド) コピー先ファイルのopen時の引数 第2引数 --- O_WRONLY | O_CREAT | O_TRUNC 第3引数 --- 0644(8進表記) とせよ。これにより、コピー先のファイル名が存在し ていたら、内容が消去されてから書き込まれる。 レポート課題3 catコマンドの以下の機能を、システムコール(open, close, read, write)を使って実装せよ。 引数無しの場合 --- 標準入力を標準出力へコピー 引数がある場合(1個以上のファイル名を引数にと る) --- それらのファイルの内容を結合したものを 標準出力に書き出す。 catコマンドを使った場合と挙動を比較し、同じであ ることを確認したのち提出すること。 レポートの提出方法 □ 下記のファイルを作成し、提出 • kadai3.c, kadai3.txt □ 提出方法 システムプログラミング講義用の課題提出用フォルダ内に あるkadai3というフォルダの中に自分の学籍番号を名前と するフォルダを作成し、その中に上記ファイルを置く。 kadai3.txt内に学籍番号、氏名、日付、および作成したプロ グラムの簡単な説明を記載する。 □ 提出期限 12月6日の23:59まで。締め切り後に提出した場合、成績へ の反映を保証しない。 参考: ライブラリ関数を使った場合 #include <stdio.h> void filecopy (FILE *fpin, FILE *fpout) { int c; while ((c = getc(fpin)) != EOF) putc (c, fpout); } この実装では1バイトず つコピーしている。複数 バイトまとめて読み込ん で書き込んだ方が速い。 int main (int argc, char * argv []) { FILE *fp; if (argc==1) filecopy (stdin, stdout); else while (--argc > 0) if ((fp = fopen (*++argv, "r")) == NULL) { printf ("cat: can't open %s\n", *argv); return 1; } else { filecopy (fp, stdout); fclose (fp); } return 0; }
© Copyright 2024 ExpyDoc