2016年7月5日火曜日

で、fcntl関数

あの時のflock関数からfcntl関数へ


flock関数が使われているコードをSolarisにそのまま移植する場合は「fcntl関数」を使うようになる。Posix的にはfcntl関数が推奨されているのでこれからファイルロックを書きたいのならfcntl関数で書く方がいいかもね。




fcntl関数


file controlという関数でファイルディスクリプタへ対していろいろな属性や制御を設定できる関数、今回はファイルのロックに関する利用方法だけに絞って確認しています。
詳しい関数の説明はmanページを見てくださいということでサンプルはgithubへ置きましたのでcloneしてmakeしてください。

https://github.com/calimakvo/fcntl.git

cuomo@karky7 ~/fcntl $ tree
.
├── Makefile
├── README.md
├── fcntl_r.c
└── fcntl_rw.c

fcntl関数もアドバイザリロック


これはflock関数と同じです

fcntl関数をファイルロックで使う


fcntl関数とflock関数の大きな違いは、ロック対象のファイルの特定の領域をバイト単位でロックが設定できるところが違います。そのためロック領域を指定するflock構造体が定義されています。

Linuxだとこれ
struct flock
  {
      short int l_type;   /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK.  */
      short int l_whence; /* Where `l_start' is relative to (like `lseek').  */
#ifndef __USE_FILE_OFFSET64
      __off_t l_start;    /* Offset where the lock begins.  */
      __off_t l_len;      /* Size of the locked area; zero means until EOF.  */
#else
      __off64_t l_start;  /* Offset where the lock begins.  */
      __off64_t l_len;    /* Size of the locked area; zero means until EOF.  */
#endif
      __pid_t l_pid;      /* Process holding the lock.  */
  };


Solarisだとこれ
/* regular version, for both small and large file compilation environment */
typedef struct flock {
        short   l_type;
        short   l_whence;
        off_t   l_start;
        off_t   l_len;          /* len == 0 means until end of file */
        int     l_sysid;
        pid_t   l_pid;
        long    l_pad[4];               /* reserve area */
} flock_t;


l_typeメンバにはF_RDLCK(READロック)、F_WRLCK(書込ロック)、F_UNLCK(ロック解除)が指定できます。l_whenceはl_startで指定される位置がファイルのどこなのかを指定するフラグ、SEEK_SET(l_startで指定された位置から)、SEEK_CUR(現在位置)、 SEEK_END(ファイルの終わり)で指定可能です。flock関数のようにファイル全体をロックするなら、l_len = 0でEOFまでロックとして

    ...
    struct flock region;
    region.l_type = F_RDLCK;
    region.l_whence = SEEK_SET;
    region.l_start = 0;   /* ファイル全体をロック */
    region.l_len = 0;     /* l_len = 0でEOFまで指定 */
    ...
    fcntl(lockfd, F_SETLK, &region);
    ...

のような感じになります、これでflock関数と同じになります。

flock関数fcntl関数の挙動の違い


flock関数は「LOCK_NB」フラグを指定しなければロックの取得ができない時点でブロックしましたがfcntl関数は基本的にブロックしない事が特徴です、この時errnoに「EAGEIN」か「EACCES」(処理系に依存)をセットしますのでその場合は再度呼び出してやる必要があります。Posixで移植性を高めるのに両方のエラーを処理できるように書いた方が望ましいそうです。

F_SETLKWを使う


ディフォルトのflock関数と同じような動作にしたい場合は「F_SETLKW」をfcntl関数の呼び出しで指定する。
...
res = fcntl(lockfd, F_SETLKW, &region);
...

「F_SETLKW」フラグはロックが取得できなかった場合、ロックの取得ができるまでブロックするような動きになります、flock関数のディフォルトの動作(「LOCK_NB」を指定しない)と同じになります。

fcntl関数とsignal


ロックの取得ができない場合はfcntl関数呼び出しでブロックするため、「EINTR」(シグナル割込)に備える必要があります。
「fcntl_rw」がブロックする状況を作って別ターミナルから「SIGUSR1」シグナルを送ると「EINTR」で戻ります。
cuomo@karky7 ~ $ kill -USR1 7348

cuomo@karky7 ~/fcntl $ ./fcntl_rw
Get EINTR
Retry lock...
...

ちょっとした注意点


ファイルのオープンの時に、「O_RDONLY」フラグでオープンした際に。fcntl関数へ「F_WRLCK」を指定して呼び出したりすると、「Bad file descriptor」を返してくるのでダメな組み合わせで利用しないようにして下さい。当たり前って言えばそのとおりデスが。

fcntl関数とNFS


勉強不足によって不明、時間がとれたら調べようと思います、すみません。

今後...


やっと「Solaris来たじゃん」って言われそうですが、やっぱりいろんなOSで走るように書くのって難しいのねって感じますね、そもそもfcntl関数の使い方がちょっと違うような気もしますが...でほんとはこの問題の元ネタはhaskellのライブラリがもとでして、そこへつながってしまいます...


汚いコードですみません...


0 件のコメント:

コメントを投稿