IPC: ファイル・ロック

ファイルのロックを行い安全にファイルを操作する。

SYNOPSIS

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
 
int fcntl(int fildes, int cmd, /* arg */ ...);

DESCRIPTION

安全にファイルをコントロールするために、操作したいファイルをロックして他のプロセスから一時的に利用できなくします。System V 系の UNIX では lockf()関数、BSD系では flock()関数がそれぞれ用意されていますが、ファイルをコントロールするには移植性や確実性で一番よいと思う fcntl()システムコールを使用して説明します。

fcntl()システムコールを使用してファイルをロックする場合、どのようにロックするか flock 構造体を使って設定しなければなりません。

    struct flock fl;
    int fd;
 
    fl.l_type = F_WRLCK;      /* F_RDLCK, F_WRLCK, F_UNLCK */
    fl.l_whence = SEEK_SET;   /* SEEK_SET, SEEK_CUR, SEEK_END */
    fl.l_start = 0;           /* Offset from l_whence */
    fl.l_len = 0;             /* length, 0 = to EOF */
    fl.l_pid = getpid();      /* our PID */
 
    fd = open("filename", O_WRONLY);
    fcntl(fd, F_SETLKW, &fl); /* F_GETLK, F_SETLK, F_SETLKW */

flock 構造体のメンバについて(ファイルのロックに関するフラグのみ解説)

l_type

ロックの種類を指定します。読み込みに対してのロックは F_RDLCK、書き込みに対してのロックは F_WRLCKを指定し、ロックの解除に F_UNLCKを指定します。

ロックの種類に対応する open()システムコールのモードも以下のように決定されます。

l_type open() モード
F_RDLCK O_RDONLY または O_RDWR
F_WRLCK O_WRONLY または O_RDWR
l_whence ロックを開始するオフセットを指定します。ファイルの始めなら SEEK_SET、現在位置からは SEEK_CUR、ファイルの終わりからなら SEEK_ENDを指定します。
l_start l_whenceで指定したところから何バイト目をオフセットにするか指定します。
l_len l_startで指定したオフセットから何バイトまでロックの対象にするか指定します。0EOF (End of File) までです。
l_pid

ロックを有効にできるプロセス ID を指定します。通常、自分自身なので getpid()関数を使用して自分のプロセス ID をセットします。

fcntl()システムコールへのコマンドについて(2番目の引数:ファイルのロックに関するコマンドのみ解説)

F_SETLKW flock構造体で指定された内容でファイルをロックします。もしロックを試みたファイルがすでにロックされていた場合は、ロックが可能になるまで待ちます。
F_SETLK F_SETLKWとほとんど同じですが、指定のファイルがすでにファイルがロックされている場合ロックが出来なかったことを示す -1 を返します。このコマンドは、ファイルのロックを解除するF_UNLCKを行う際にも使用されます。
F_GETLK ファイルがロックされているかチェックする際に使用します。

ロックの解除は、プログラム中のロックを解除したいところで以下のように指定します。

    fl.l_type = F_UNLCK;      /* tell it to unlock the region */
    fcntl(fd, F_SETLK, &fl);  /* set the region to unlocked */

SAMPLE

パイプに対して書き込んだ内容を読み込みます。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
 
int main()
{
    struct flock fl;
    int fd;
 
    fl.l_type   = F_WRLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start  = 0;
    fl.l_len    = 0;
    fl.l_pid    = getpid();
 
    if ((fd = open("lock.txt", O_RDWR)) == -1) {
        perror("open");
        exit(1);
    }
 
    printf("Press <Enter> to try to get lock: ");
    getchar();
    fputs("waiting...", stdout);
 
    if (fcntl(fd, F_SETLKW, &fl) == -1) {
        perror("fcntl");
        exit(1);
    }
 
    puts("Locked.");
    printf("Press <Enter> to release lock: ");
    getchar();
 
    fl.l_type = F_UNLCK;
    if (fcntl(fd, F_SETLK, &fl) == -1) {
        perror("fcntl");
        exit(1);
    }
 
    puts("Unlocked.");
 
    close(fd);
 
    return 0;
}

これをコンパイルし実行すると以下のようになります。

  << SHELL-1 >>
    # gcc flock_sample.c -o flock_sample
    # touch lock.txt
    # ./flock_sample
    Press <Enter> to try to get lock: <Enter>
    waiting...Locked.
    Press <Enter> to release lock: 

上記の状態で、ファイルがロックされています。別のシェルでもう一度プログラムを起動してみます。

  << SHELL-2 >>
    # ./flock_sample
    Press <Enter> to try to get lock: <Enter>
    waiting...

SHELL-1 でファイルをロックしているため、SHELL-2 ではロックが解除されるまで待っています。ここで、SHELL-1 で Enter キーをタイプしてロックを解除すると、SHELL-2 でファイルをロックします。

SEE ALSO

fcntl(2), open(2)

 

戻る