FLOCK(1) H. Peter Anvin FLOCK(1) NAME flock - Manage locks from shell scripts SYNOPSIS flock [-sxon] [-w timeout] lockfile [-c] command... flock [-sxon] [-w timeout] lockdir [-c] command... flock [-sxun] [-w timeout] fd DESCRIPTION This utility manages flock(2) locks from within shell scripts or the command line. The first and second forms wraps the lock around the executing a command, in a man・ er similar to su(1) or newgrp(1). It locks a specified file or directory, which is created (assuming appropriate permissions), if it does not already exist. The third form is convenient inside shell scripts, and is usually used the follow・ ng manner: ( flock -s 200 # ... commands executed under lock ... ) 200>/var/lock/mylockfile ...オンラインマニュアルの例では、サブシェルを起動していますが、次のようにすれば、シェルスクリプト全体をシリアライズすることも可能です。
#!/bin/bash exec 9>>/var/lock/mylockfile flock -n 9 if [ $? -ne 0 ] ; then # ロックできなければ、失敗(exit 1)させるかどうかは、処理の内容次第です。 # ロック解放を待ち合わせるのなら、-n オプションを付けなければ良い。 fi # # 排他の必要な処理を記述。 # flock -u 9 # スクリプトに対応するプロセスが終了すれば、 # カーネルが自動的にロック解放しますが、明示的に記述する。 exit 0exec 9>>/var/lock/mylockfile という書き方をすると、/var/lock/mylockfile を追記モードで open して、その fd を 9 番に割り当てます。シェル内部では、dup2(2) が使われます。
CentOS 4 以下では、flock コマンドはまだ提供されておらず、lockfile という似たコマンドがあるようです。しかし、lockfile のほうは、原始的なテクニックが使われており、flock の方が信頼性は高いと思われます。
2011-09-04追加
flock コマンドは、flock(2) システムコールに依存しており、
2011-09-08 実験してみたところでは動作しました。追記内容を参照してください。
NOTES flock(2) does not lock files over NFS. Use fcntl(2) instead: that does work over NFS, given a sufficiently recent version of Linux and a server which supports locking.flock はファイルシステム毎に実装 (struct file operations の flock メンバ) することができ、確かに NFS では、次のように定義されていました。
以下、2.6.18-194.el5 (CentOS 5.5) から抜粋です。
54 const struct file_operations nfs_file_operations = { 55 .llseek = nfs_file_llseek, 56 .read = do_sync_read, 57 .write = do_sync_write, 58 .aio_read = nfs_file_read, 59 .aio_write = nfs_file_write, 60 .mmap = nfs_file_mmap, 61 .open = nfs_file_open, 62 .flush = nfs_file_flush, 63 .release = nfs_file_release, 64 .fsync = nfs_fsync, 65 .lock = nfs_lock, 66 .flock = nfs_flock, 67 .sendfile = nfs_file_sendfile, 68 .check_flags = nfs_check_flags, 69 }; ... 639 /* 640 * Lock a (portion of) a file 641 */ 642 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) 643 { 644 dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n", 645 filp->f_dentry->d_inode->i_sb->s_id, 646 filp->f_dentry->d_inode->i_ino, 647 fl->fl_type, fl->fl_flags); 648 649 /* 650 * No BSD flocks over NFS allowed. 651 * Note: we could try to fake a POSIX lock request here by 652 * using ((u32) filp | 0x80000000) or some such as the pid. 653 * Not sure whether that would be unique, though, or whether 654 * that would break in other places. 655 */ 656 if (!(fl->fl_flags & FL_FLOCK)) 657 return -ENOLCK; 658 "fs/nfs/file.c"flock(2) システムコールのコードは次のようになっています。
1590 /** 1591 * sys_flock: - flock() system call. 1592 * @fd: the file descriptor to lock. 1593 * @cmd: the type of lock to apply. 1594 * 1595 * Apply a %FL_FLOCK style lock to an open file descriptor. 1596 * The @cmd can be one of 1597 * 1598 * %LOCK_SH -- a shared lock. 1599 * 1600 * %LOCK_EX -- an exclusive lock. 1601 * 1602 * %LOCK_UN -- remove an existing lock. 1603 * 1604 * %LOCK_MAND -- a `mandatory' flock. This exists to emulate Windows Share Modes. 1605 * 1606 * %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other 1607 * processes read and write access respectively. 1608 */ 1609 asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) 1610 { 1611 struct file *filp; 1612 struct file_lock *lock; 1613 int can_sleep, unlock; 1614 int error; 1615 1616 error = -EBADF; 1617 filp = fget(fd); 1618 if (!filp) 1619 goto out; 1620 1621 can_sleep = !(cmd & LOCK_NB); 1622 cmd &= ~LOCK_NB; 1623 unlock = (cmd == LOCK_UN); 1624 1625 if (!unlock && !(cmd & LOCK_MAND) && !(filp->f_mode & 3)) 1626 goto out_putf; 1627 1628 error = flock_make_lock(filp, &lock, cmd); 1629 if (error) 1630 goto out_putf; 1631 if (can_sleep) 1632 lock->fl_flags |= FL_SLEEP; 1633 1634 error = security_file_lock(filp, cmd); 1635 if (error) 1636 goto out_free; 1637 1638 if (filp->f_op && filp->f_op->flock) 1639 error = filp->f_op->flock(filp, 1640 (can_sleep) ? F_SETLKW : F_SETLK, 1641 lock); 1642 else 1643 error = flock_lock_file_wait(filp, lock); ... "fs/locks.c"NFS の場合は、f_op->flock が定義されているので、1639行目が実行され、
ext3 等の場合は、f_op->flock が未定義なので、1643行目が実行されます。
なお、lock->fl_flags に FL_FLOCK を設定しているのは、1628行目です。
278 /* Fill in a file_lock structure with an appropriate FLOCK lock. */ 279 static int flock_make_lock(struct file *filp, struct file_lock **lock, 280 unsigned int cmd) 281 { 282 struct file_lock *fl; 283 int type = flock_translate_cmd(cmd); 284 if (type < 0) 285 return type; 286 287 fl = locks_alloc_lock(); 288 if (fl == NULL) 289 return -ENOMEM; 290 291 fl->fl_file = filp; 292 fl->fl_pid = current->tgid; 293 fl->fl_flags = FL_FLOCK; 294 fl->fl_type = type; 295 fl->fl_end = OFFSET_MAX; 296 297 *lock = fl; 298 return 0; 299 } "fs/locks.c"
2011-09-08追記
fs/nfs/file.c の 656 行目を条件を読み間違えていました。ここは、FL_FLOCK が設定されていないのに、ここに来た場合に ENOLCK を返すというだけでした。
それで、続きを読んでみると、POSIX のロックを使ってシミュレートするよ とあります。
639 /* 640 * Lock a (portion of) a file 641 */ 642 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) 643 { 644 dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n", 645 filp->f_dentry->d_inode->i_sb->s_id, 646 filp->f_dentry->d_inode->i_ino, 647 fl->fl_type, fl->fl_flags); 648 649 /* 650 * No BSD flocks over NFS allowed. 651 * Note: we could try to fake a POSIX lock request here by 652 * using ((u32) filp | 0x80000000) or some such as the pid. 653 * Not sure whether that would be unique, though, or whether 654 * that would break in other places. 655 */ 656 if (!(fl->fl_flags & FL_FLOCK)) 657 return -ENOLCK; 658 659 /* We're simulating flock() locks using posix locks on the server */ 660 fl->fl_owner = (fl_owner_t)filp; 661 fl->fl_start = 0; 662 fl->fl_end = OFFSET_MAX; 663 664 if (fl->fl_type == F_UNLCK) 665 return do_unlk(filp, cmd, fl); 666 return do_setlk(filp, cmd, fl); 667 } "fs/nfs/file.c"それじゃあ、flock コマンドが NFS を越えて正しく動作するのかというと、POSIX ロックと BSD ロック (flock) の違い (POSIX ロックは fork を越えられない) により正しく動作しないのでは? っと思い、テストプログラムで確かめてみました。CentOS 5 で確認しています。
#!/bin/bash exec 9>>./flock.test.lock flock -n 9 if [ $? -ne 0 ] ; then echo "fail" 1>&2 exit 1 fi sleep 30 flock -u 9 exit 0
# df -TP . Filesystem Type 1024-blocks Used Available Capacity Mounted on xx.xx.xx.xx:/ nfs 5921856 5455872 165176 98% /mnt_nfs # /tmp/flock.sh & /tmp/flock.sh [1] 6452 fail2つ同時起動で、2つ目が失敗してますから、ちゃんとロック出来ているようです。
どうなっているのか理解出来ていませんが、NFS の開発者の方は、flock も視野に実装してくれたということでしょうかね。
2011-09-09追記
CentOS 6 を NFS サーバにして、flock を試すとうまく行かず、次のようなエラーになってしまいました。クライアントは CentOS 5 です。
# ./flock.sh flock: 9: No locks available failサーバ側を見てみると、次のエラーが出ていました。
Sep 8 23:39:14 centos6 rpc.statd[6061]: No canonical hostname found for xx.xx.xx.xx Sep 8 23:39:14 centos6 rpc.statd[6061]: STAT_FAIL to centos6 for SM_MON of xx.xx.xx.xx Sep 8 23:39:14 centos6 kernel: lockd: cannot monitor xx.xx.xx.xx調べてみると、次の情報が見つかりました。
http://dfwarden.blogspot.com/2011/02/psa-nfs-locking-in-rhel6-needs-reverse.html
確かに、サーバ側 (CentOS 6) の /etc/hosts に、クライアントの IP アドレスを登録したら、flock 出来るようになりました。信頼性・セキュリティの強化が図られているということでしょうかね。
なお、lockd: cannot monitor のメッセージは、クライアント側で nfslock サービスが上がっていない場合にも出るメッセージなようです。メモ。
例示のシェルスクリプトですが「flock -n 9」では flock(1) が終了するとロックは開放されます。その後の処理が排他されませんよ。
返信削除fumiyas さん、コメントありがとうございました。
削除[root@my41 ~]# uname -a
Linux my41 2.6.18-308.el5 #1 SMP Tue Feb 21 20:06:06 EST 2012 x86_64 x86_64 x86_64 GNU/Linux
[root@my41 ~]# cat /etc/redhat-release
CentOS release 5.8 (Final)
[root@my41 ~]# exec 9>>/var/lock/mylockfile
[root@my41 ~]# ls -li /var/lock/mylockfile
2288458 -rw-r--r-- 1 root root 0 Sep 9 08:34 /var/lock/mylockfile
[root@my41 ~]# grep 2288458 /proc/locks
[root@my41 ~]# flock -n 9
[root@my41 ~]# grep 2288458 /proc/locks
1: FLOCK ADVISORY WRITE 6586 08:05:2288458 0 EOF
このようにロック取得できると思いますが、如何でしょうか。
あれ、本当だ。マニュアル flock(2) に書いてありました。
返信削除私の認識が間違っていました。すみません。