Linuxシステムコールの勉強(その6)

Linuxシステムコール

Linuxシステムコール

前回はこちら


今回からファイル操作についてまとめます。
通常、C言語を使ったファイル操作と言うとstdio.hに宣言されているライブラリ関数を使用することがほとんどです。
# fopen()やfprintf(),fscanf(),fputs(),fgets()が該当します


このようなライブラリ関数には以下のような工夫がされていて使いやすくなっています。

    1. データのバッファリング(まとめて書き込むこと)を使ったりして効率がいい
    2. データの型変換などの機能がついている


これに対してシステムコールとして用意されている関数はより原子的な機能しか有していません。例えば

    1. n文字入力する(n=1,2,3...)
    2. n文字出力する(n=1,2,3...)
    3. 入出力位置を変更する

といった機能です。これを低水準ファイル入出力と呼びます。
# 対して前者のライブラリ関数を高水準ファイル入出力と呼びます


高水準ファイル入出力の良い点としては、上記のとおり機能が豊富である事や処理効率が高い事が挙げられますが、それ以上にC言語標準のライブラリを使用することで開発環境に依存しない開発を行えます。
逆に低水準ファイル入出力の良い点としては、OSに依存している==ライブラリよりもハードウェアに近い機能を利用出来る点です。そもそも各種ライブラリ関数はより低水準なファイル入出力を使って実装されているので、当たり前の話といえばそのとおりですが...。


私のイメージではライブラリ関数を使うことはオートマの車を運転する事に近くて、システムコールを使うことはマニュアルの車を運転する事に近いです。どちらが良いのかはその場その場で変わってきます。


今回は低水準ファイル入出力についてのみ扱います。高水準の方はC言語の入門書を読んでください。
# 私はこれ↓で勉強しました


C言語によるプログラミング 基礎編

C言語によるプログラミング 基礎編


使用するシステムコールの定義はこちら。

/*** ファイルを開く ***/
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <fcntl.h>
// 
// 第一引数 開くファイル名(絶対パスで指定)
// 第二引数 アクセスフラグ
// 第三引数 ファイルに設定する許可(省略可)
//
// 返却値
//  成功時 : 開いたファイルのファイルディスクリプタ
//  失敗時 : -1
int open(const char *path, int flags);
int open(const char *path, int flags, mode_t mode);


/*** ファイルを閉じる ***/
// #include <unistd.h>
// 
// 第一引数 閉じるファイルディスクリプタ
//
// 返却値
//  成功時 :  0
//  失敗時 : -1
int close(int fd);


/*** ファイルを読む ***/
// #include <unistd.h>
// 
// 第一引数 読み込むファイルのファイルディスクリプタ
// 第二引数 読み取ったデータの格納先アドレス
// 第三引数 読み取りバイト数
//
// 返却値
//  1以上 読み込んだデータバイト数
//  0   ファイルの末尾に達している
// -1   エラーが発生
ssize_t read(int fd, void *buf, size_t count);



/*** ファイルに書き込む ***/
// #include <unistd.h>
// 
// 第一引数 書き込むファイルのファイルディスクリプタ
// 第二引数 書き込むデータの格納元アドレス
// 第三引数 書き込みバイト数
//
// 返却値
//  0以上 読み込んだデータバイト数
// -1   エラーが発生
ssize_t write(int fd, void *buf, size_t count);


まずはファイルを開き、データを読み取り、後始末をする部分を説明します。

/**********************************************
 * dataread.c
 * 引数で指定されたファイルを表示する
 **********************************************/

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int     fd;
    char    c ;
    ssize_t len;
    
    // 引数が足りなかったらエラーを表示して終了 //
    if ( argc < 2 )
    {
        fprintf(stderr,"usage: %s filename\n",basename(argv[0]) );
        return 1;
    }

    // ファイルを読み取り専用で開く際にエラー //
    if ( ((fd = open(argv[1],O_RDONLY)) == -1 )
    {
        perror("open()");
        return 2;
    }

    do {
        // ファイルを1文字ずつ読み込む //
        len = read(fd, &c, 1);
        if ( len < 0 )
        {
            perror("read()");
        }
    } while ( len > 0 );

    // ファイルを閉じる //
    close(fd);

    if ( len == 0 ) return 0;
    else            return 3;
}


実行結果はこちら。

[itotto@ ~/]$ cat samplefile.dat
hoge hoge
fuga fuga
foo foo
bar bar

[itotto@ ~/]$ gcc -o dataread dataread.c && ./dataread samplefile.dat
hoge hoge
fuga fuga
foo foo
bar bar

[itotto@ ~/]$ 


open()の返却値を格納しているfd(int型)をファイルディスクリプタと呼びます。
ファイルディスクリプタLinux Kernelがファイルを管理するための情報を格納しておく構造体の番号です。そのため、ファイル操作を行う場合には操作したいファイルのファイルディスクリプタがいくつなのかということを知っておく必要があります。これの番号を介してファイル操作対象を指定します。


また、open()の第二引数で指定するフラグ(上のソースだとO_RDONLY)はファイルを開く時にどのようなモードで開くのかを指定するフラグです(fcntl.hに定義)。よく使うフラグは以下のとおりです。

フラグ名 役割
O_RDONLY ファイルを読出専用で開く事を要求
O_WRONLY ファイルを書込専用で開く事を要求
O_RDWR ファイルを読書容量で開く事を要求


また、このフラグについて追加で指定出来るフラグがあります。

追加用のフラグ名 意味
O_CREAT ファイルが存在しないと作成する
O_RDONLYと併用しても大丈夫
O_EXCL ファイルがない場合には作成するが、ある場合にはエラーになる
(該当処理でファイルが作成される事を保証する際に使う)
O_TRUN ファイルが既にある場合、その内容を削除する
O_APPEND 追加モードでファイルを開く


追加で指定する場合には上記フラグに対して論理和を取って指定します。

[例]

// ファイルを書込用で、かつ追加モードで開く //
open("/home/itotto/hogehoge.txt",O_WRONLY|O_APPEND);


さて。
この追加用のフラグのO_CREATを指定した場合にはファイルのpermissionを第三引数として指定する必要があります。

mode 意味
S_IRWXU 00700 所有者の読書実行を許可
S_IRUSR 00400 所有者の読み込みを許可
S_IREAD
S_IWUSR 00200 所有者の書き込みを許可
S_IWRITE
S_IXUSR 00100 所有者の実行を許可
S_IEXEC
S_IRWXG 00070 グループの読書実行を許可
S_IRGRP 00040 グループの読み込みを許可
S_IWGRP 00020 所有者の書き込みを許可
S_IXGRP 00010 所有者の実行を許可
S_IRWXO 00007 上記以外の読書実行を許可
S_IROTH 00004 上記以外の読み込みを許可
S_IWOTH 00002 上記以外の書き込みを許可
S_IXOTH 00001 上記以外の実行を許可


UNIXのファイルパーミッションの考えを理解していれば特に悩むところはありませんが、プログラムを実行したユーザのumaskの影響を受ける点については注意してください。


次回はファイル入出力からさらに掘りさげてまとめます。


次へ進む