レコードロック

レコードロックとファイルロック

  • Unixカーネルは、ファイル中のレコードの概念を全く持たず、レコードの解釈は特定のファイルを読み書きするアプリケーションに一任されている

  • アプリケーションは、ファイル中のバイト範囲をロック・アンロックする

  • ロック可能なオブジェクトの大きさを粒度という
  • Posixのfcntlレコードロック

    #include <fcntl.h>
    
    int fcntl(int fd, int cmd, ... /* struct flock *arg */ );
    
    戻り値:成功の場合はcmdに依存、エラーなら-1

    レコードロックを操作する目的でこの関数を呼び出す場合、cmd引数には、3種類の値が用いられる。この3種類のコマンドは、第3引数argが、flock構造体へのポインタであることを要求する。

    struct flock {
        short l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */
        short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
        off_t l_start; /* 開始位置の相対オフセット(バイト単位) */
        off_t l_len; /* バイト数;ゼロはファイルの終わりまでを表す。 */
        pid_t l_pid; /* F_GETLKが返すプロセスID */
    };



  • 3種類のコマンド

  • F_SETLKargが指す構造体によって指定されるロックの取得(l_typeがF_RDLCKまたはF_WRLCKの場合)と解放(l_typeがF_UNLCKの場合)を行う
    F_SETLKWF_SETLKと似ているが、呼び出しプロセスに対するロック取得が拒否された場合に、そのロックが取得できるまでブロックするところが異なる。(コマンド名の最後のWは、"wait"を表している。)
    F_GETLKargが指す構造体が指定するロックを検査し、既存のロックが新しく指定するロックの取得を妨げるかどうかを調べる。新しいロックの取得を妨げる既存のロックが存在しない場合は、flock構造体のl_typeメンバにF_UNLCKが設定される。新しいロックの取得を妨げる既存のロックが存在する場合、その既存のロックを保持しているプロセスIDを含めた既存ロックに関する情報が、argが指すflock構造体に返される。



    /* ファイルロックの例 */
    
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/stat.h>
    
    #define SEQFILE "seqno"
    #define MAXLINE 16
    
    #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
    
    void
    my_lock(int fd)
    {
        /* ファイル全体を書き込みロック */
        struct flock lock;
        lock.l_type = F_WRLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        fcntl(fd, F_SETLKW, &lock);
    }
    
    void
    my_unlock(int fd)
    {
        struct flock lock;
        lock.l_type = F_UNLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = 0;
        lock.l_len = 0;
        fcntl(fd, F_SETLK, &lock);
    }
    
    int
    main()
    {
        int fd;
        long i, seqno;
        pid_t pid;
        ssize_t n;
        char line[MAXLINE + 1];
    
        pid = getpid();
        fd = open(SEQFILE, O_RDWR, FILE_MODE);
        for (i = 0; i < 1000; i++) {
            my_lock(fd);
            lseek(fd, 0L, SEEK_SET);
            n = read(fd, line, MAXLINE);
            line[n] = '\0';
            seqno = atoi(line);
            printf("pid = %ld, seq = %ld\n",
                (long)pid, seqno);
            seqno++;
            snprintf(line,
                sizeof(line),
                "%ld",
                seqno);
            lseek(fd, 0L, SEEK_SET);
            write(fd, line, strlen(line));
            my_unlock(fd);
        }
    
        close(fd);
        return 0;
    }
    




    Posix.1では、O_CREATフラグとO_EXCLフラグ(排他オープン)を伴って呼び出されたopenは、指定されたファイルが既に存在する場合には、エラーを返すことが保証されている。
    既存のファイルの存在検査とファイルの作成(存在しない場合)が、ほかのプロセスに対してAtomicに行われることが保障されているため、この方法で作成したファイルをロックとして用いることが出来る。

    /* ロックファイルの例 */
    
    #define LOCKFILE "/tmp/seqno.lock"
    
    void
    my_lock(int fd)
    {
            int tempfd;
            while ( (tempfd = open(LOCKFILE, O_RDWR|O_CREAT|O_EXCL, FILE_MODE)) < 0 ) {
                    if (errno != EEXIST) {
                        /* ロックは既に保持されている。 */
                            perror("open");
                    }
            }
            /* ファイルがオープンできたらロック取得完了 */
            close(tempfd);
    }
    
    void
    my_unlock(int fd)
    {
            unlink(LOCKFILE);
    }