#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);
成功すると、新しいディスクリプタが返される。
詳細は dup(2) を参照のこと。
ファイル状態フラグとその意味は open(2) で説明されている。
struct flock { ... short l_type; /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */ short l_whence; /* How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END */ off_t l_start; /* Starting offset for lock */ off_t l_len; /* Number of bytes to lock */ pid_t l_pid; /* PID of process blocking our lock (F_GETLK only) */ ... };この構造体の l_whence, l_start, l_len フィールドで、ロックを行いたいバイト範囲を指定する。 l_start はロックを行う領域の開始オフセットである。 その意味は l_whence により異なる: l_whence が SEEK_SET の場合はファイルの先頭からのオフセット、 l_whence が SEEK_CUR の場合は現在のファイルオフセットからのオフセット、 l_whence が SEEK_END の場合はファイルの末尾からのオフセットと解釈される。 後ろの2つの場合には、 ファイルの先頭より前にならない範囲で、 l_start に負の値を指定することができる。
l_len はロックを行う領域のバイト数を示し、非負の整数で指定する (下記の 注意の章も参照)。 バイト数の指定は、ファイルの末尾を超えた範囲まで行ってもよいが、 ファイルの先頭より前になってはいけない。 l_len が 0 の場合は特別な意味を持つ: l_whence and l_start で指定される位置からファイルの末尾までの全てのバイトをロックする (ファイルがどんなに大きくなったとしてもファイルの末尾までロックする)。 l_type フィールドは、ファイルに対して読み出しロック (F_RDLCK) と書き込みロック (F_WRLCK) のどちらを 設定するかを指定する。 ファイルのある領域に対して、読み出しロック (共有ロック) を保持できる プロセス数に制限はないが、書き込みロック (排他ロック) を保持できる のは一つのプロセスだけである。排他ロックを設定すると、(共有ロックか 排他ロックにかかわらず) 他のロックは何も設定できない。 一つのプロセスは、ファイルのある領域に対して一種類のロックしか保持できない。 新規のロックがロックが設定されている領域に対して適用されると、既存のロック は新規のロックの種別に変換される (新規のロックで指定されたバイト範囲が既存ロックの範囲と一致する場合以外では、 変換の過程で既存のロックの分割、縮小、結合が行われることがある)。
アドバイザリ・ロックに強制力はなく、協調して動作するプロセス間でのみ 有効である。
強制ロックは全てのプロセスに対して効果がある。 あるプロセスが互換性のない強制ロックが適用されたファイル領域に対して (read(2) や write(2) により) 互換性のないアクセスを実行しようとした場合、 アクセスの結果は そのファイルのオープンファイル記述で O_NONBLOCK フラグが有効になっているかにより決まる。 O_NONBLOCK フラグが有効になっていないときは、ロックが削除されるか、 ロックがアクセスと互換性のあるモードに変換されるまで、 システムコールは停止 (block) される。 O_NONBLOCK フラグが有効になっているときは、システムコールはエラー EAGAIN か EWOULDBLOCK で失敗する。
強制ロックを使用するためには、ロック対象のファイルが含まれるファイルシステム と、ロック対象のファイル自身の両方について、強制ロックが有効になっていなけれ ばならない。ファイルシステムについて強制ロックを有効にするには、 mount(8) に "-o mand" オプションを渡すか、 mount(2) に MS_MANDLOCK フラグを指定する。ファイルについて強制ロックを有効にするには、 そのファイルのグループ実行許可 (group execute permission) を無効とし、 かつ set-group-ID 許可ビットを有効にする (chmod(1) と chmod(2) を参照)。
fcntl() の F_SETFL コマンドを使用してファイルディスクリプタに O_ASYNC 状態フラグを設定した場合には、そのファイルディスクリプタへの 入出力が可能になる度に SIGIO シグナルが送られる。 F_SETSIG は SIGIO 以外の別のシグナルの配送を受けられるように するのにも使うことができる。 許可 (permission) のチェックで失敗した場合には、 シグナルは黙って捨てられる。
F_SETOWN により指定された所有者のプロセス (またはプロセスグループ) に シグナルを送る際には、 kill(2) に書かれているのと同じ許可のチェックが行われる。 このとき、シグナルを送信するプロセスは F_SETOWN を使ったプロセスである (但し、下記の「バグ」の章を参照のこと)。
ファイルディスクリプタがソケットを参照している場合は、 F_SETOWN を使用して、ソケットに帯域外 (out-of-band) データが届いた時に SIGURG シグナルを配送する相手を選択することもできる (SIGURG が送られた場合には select(2) がソケットが「特別な状態」にあると報告することだろう)。
スレッドグループをサポートしているスレッドライブラリ (例えば NPTL) を 使って動作しているマルチスレッド・プロセスで F_SETSIG に 0 以外の値を指定した場合、 F_SETOWN に正の値を渡すと、その意味が違ってくる: プロセス全体を示すプロセスID ではなく、プロセス内の特定の スレッドを示すスレッドID と解釈される。 したがって、 F_SETSIG を使う場合には、きちんと結果を受け取るには、 F_SETOWN に渡す値を getpid(2) ではなく gettid(2) の返り値にする必要があるだろう。 (現状の Linux スレッド実装では、メイン・スレッドのスレッドID は そのスレッドのプロセスID と同じである。つまり、 シグナル・スレッドのプログラムではこの場合 gettid(2) と getpid(2) は全く同じように使うことができる。) ただし、注意すべき点として、この段落で述べたことは、 ソケットの帯域外データが届いたときに生成される SIGURG シグナルにはあてはまらない。 このシグナルは常にプロセスかプロセスグループに送られ、 送信先は F_SETOWN に渡された値にしたがって決める。 また、以下の点にも注意すること。 Linux では一つのプロセスに対してキューに入れられるリアルタイム シグナルの数に上限が設けられており (getrlimit(2) と signal(7) を参照)、この上限に達するとカーネルは SIGIO シグナルを配送する。この SIGIO シグナルは、指定されたスレッドではなくプロセス全体に送られる。
また、 F_SETSIG に 0 以外の値を渡すと、シグナルの受信者をプロセス全体から プロセス内の特定のスレッドに変更される。詳細は F_SETOWN の説明を参照のこと。
F_SETSIG にゼロ以外の値を設定し、シグナルハンドラに SA_SIGINFO フラグを設定すると、 (sigaction(2) を参照) I/O イベントに関する追加の情報が siginfo_t 構造体でシグナルハンドラへ渡される。 si_code フィールドが示すシグナルの原因が SI_SIGIO である場合、 si_fd フィールドにはイベントに対応するファイルディスクリプタが入っている。 それ以外の場合は、どのファイルディスクリプタが利用可能かを示す情報は ないので、どのファイルディスクリプタで I/O が可能かを判断するためには 通常の機構 (select(2), poll(2), O_NONBLOCK を設定した read(2) など) を使用しなければならない。
リアルタイムシグナル (値が SIGRTMIN 以上) を選択している場合は、 同じシグナル番号を持つ複数の I/O イベントがキューに入ることがある (キューに入れるかどうかは利用可能なメモリに依存している)。 上記と同様、 SA_SIGINFO が設定されている場合、シグナルハンドラのための追加の情報が得られる。
これらの機構を使用することで、ほとんどの場合で select(2) や poll(2) を使用せずに完全な非同期 I/O を実装することができる。
O_ASYNC, F_GETOWN, F_SETOWN の使用は BSD と Linux に特有である。 F_GETSIG と F_SETSIG は Linux 固有である。POSIX には、同様のことを行うために、非同期 I/O と aio_sigevent 構造体がある。Linux では、GNU C ライブラリ (Glibc) の一部として これらも利用可能である。
あるプロセス ("lease folder") が F_SETLEASE で設定されたリースと矛盾するような open(2) や truncate(2) を実行した場合、 そのシステムコールはカーネルによって停止され、 カーネルは lease holder にシグナル (デフォルトでは SIGIO) を送って通知を行う。 lease holder はこのシグナルを受信したときにはきちんと対応すべきである。 具体的には、別のプロセスがそのファイルにアクセスするための準備として 必要な後片付け (例えば、キャッシュされたバッファのフラッシュ) を すべて行ってから、そのファイルのリースの削除または格下げを行う。 リースを削除をするには、 arg に F_UNLCK を指定して F_SETLEASE を実行する。 ファイルに書き込みリースを保持をしていて、 lease breaker が読み出し用にそのファイルをオープンしようとしている場合、 適用するリースを読み出しリースに格下げすれば十分である。 これをするには、 arg に F_RDLCK を指定して F_SETLEASE を実行する。
lease holder が /proc/sys/fs/lease-break-time で指定された秒数以内にリースの格下げか削除を行えなかった場合、 カーネルは強制的にその lease holder のリースを削除もしくは格下げを行う。
一度リースの削除か格下げが自発的もしくは強制的に行われると、 lease breaker がまだシステムコールを再開していない場合には、 カーネルが lease breaker のシステムコールの続行を許可する。
lease breaker が実行した open(2) や truncate(2) が停止中にシグナルハンドラにより中断された場合、 そのシステムコールは EINTR エラーで失敗するが、上で述べた他の処理は そのまま行われる。 open(2) や truncate(2) が停止中に lease breaker がシグナルにより kill された場合、 上で述べた他の処理はそのまま行われる。 lease breaker が open(2) を呼ぶ際に O_NONBLOCK フラグを指定した場合、そのシステムコールは EWOULDBLOCK エラーで直ちに失敗するが、上で述べた他の処理はそのまま行われる。
lease holder への通知に使われるデフォルトのシグナルは SIGIO だが、 fcntl() の F_SETSIG コマンドで変更することができる。 F_SETSIG コマンドが実行され (SIGIO を指定された場合も含む)、 SA_SIGINFO フラグ付きでシグナルハンドラが設定されている場合には、 ハンドラの第二引き数として siginfo_t 構造体が渡され、この引き数の si_fd フィールドには別のプロセスがアクセスしたリース設定済みファイルの ディスクリプタが入っている (この機能は複数のファイルに対してリースを設定する場合に有用である)。
ビット | 説明 (ディレクトリのイベント) |
DN_ACCESS | |
DN_MODIFY | ファイルの内容が変更された (write, pwrite, |
writev, truncate, ftruncate) | |
DN_CREATE | ファイルが作成された (open, creat, mknod, |
mkdir, link, symlink, rename) | |
DN_DELETE | ファイルが削除 (unlink) された (unlink, |
別のディレクトリへの rename, rmdir) | |
DN_RENAME | ディレクトリ内でのファイル名の変更があった (rename) |
DN_ATTRIB | ファイル属性が変更された (chown, chmod, utime[s]) |
(上記の定義を利用するには _GNU_SOURCE 機能検査マクロを定義しなければならない。)
ディレクトリの変更通知は通常「一回限り (one-shot)」であり、 アプリケーション側でその後さらに通知を受信したい場合は 再登録しなければならない。 arg に DN_MULTISHOT が含まれていた場合には、 変更通知は明示的に解除されるまで有効状態が継続する。
F_NOTIFY 要求は積算されていく。つまり、 arg で指定されたイベントがすでにモニタされている イベント集合に加算される形になる。 すべてのイベントの通知を無効にするには、 arg に 0 を指定して F_NOTIFY を呼び出す必要がある。
通知はシグナルの配送で行われる。 デフォルトのシグナルは SIGIO だが、 fcntl() の F_SETSIG コマンドで変更することができる。 後者の場合には、 (SA_SIGINFO フラグ付きでシグナルハンドラが設定されている場合には) ハンドラの第二引き数として siginfo_t 構造体が渡され、この構造体の si_fd フィールドには通知の行われたファイルディスクリプタが入っている (この機能は複数のディレクトリに対して通知を設定する場合に有用である)。
特に DN_MULTISHOT を使う場合は、通知にはリアルタイムシグナルを使うべきである。 それは、リアルタイムシグナルを使うことで、複数の通知をキューに入れる ことができるからである。
注意: 新しくアプリケーションを書く際には、(カーネル 2.6.13 以降で利用可能となった) inotify インタフェースを使用することを検討すべきである。 inotify はファイルシステムイベントの通知を取得するための上位インタフェースである。 inotify(7) を参照。
エラーの時は -1 が返され、 errno に適切な値が設定される。
F_GETSIG, F_SETSIG, F_NOTIFY, F_GETLEASE, F_SETLEASE は Linux 固有である (これらの定義を有効にするには _GNU_SOURCE マクロを定義すること)。
カーネル 2.0 以降では、 flock(2) と fcntl(2) が設定するロック種別の間に相互作用はない。
POSIX.1-2001 では l_len を負の値にすることができる (この場合、ロックされるバイト位置は l_start+l_len 以上 l_start-1 以下となる)。 この動作は Linux 2.4.21 および 2.5.49 以降でサポートされている。
システムによっては、 struct flock に上記以外のフィールドがあるものもある (例えば l_sysid)。 はっきりと言えることは、ロックを保持しているプロセスが別のマシンに存在 する場合には、 l_pid だけはあまり役にたたないだろうということである。
Linux 2.4 以前では、非特権プロセスが F_SETOWN を使って、ソケットのファイルディスクリプタの所有者に 呼び出し元以外のプロセス (やプロセスグループ) を指定すると 発生するバグがある。この場合、 呼び出し元が所有者として指定したプロセス (やプロセスグループ) に シグナルを送る許可を持っていたとしても、 fcntl() が -1 を返し errno に EPERM を設定することがある。 このエラーが返ったにもかかわらず、ファイルディスクリプタの所有者 は設定され、シグナルはその所有者に送られる。