Linux下fcntl实现建议锁和强制锁

近日小温下APUE,发现Linux下的 fcntl 实现强制锁的功能好像都没试验过,简单做个测试。

首先用 fcntl 实现建议锁(Advisory locking),比较简单,贴个最简单的代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char ** argv)
{
	int fd = 0, ret = 0;
	struct flock lock = {0};

	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;

	fd = open("test.file", O_RDWR | O_CREAT);
	printf("before flock\n");
	ret = fcntl(fd, F_SETLKW, &lock);
	printf("after flock, ret: %d\n", ret);
	getchar();
	close(fd);

	return 0;
}

关键的几句已高亮显示,用 F_SETLKW 等待写的锁,很好测试,分别开一个终端运行一次就可以测试出来。

Linux 同时实现了 POSIX 的 fcntl 锁函数,BSD 的 flock 函数,SVR4 的 lockf 函数,这些默认都是建议锁。

这个简单的实现只是建议锁,需要每个对文件的操作的进程都遵循同样的锁操作才能起作用,这些进程称为合作进程(Cooperative processes),但实际使用时会有例外的情况,如果另外开一个终端窗口直接写 test.file 文件,会发现可以直接写,因为这种写没有锁操作,不是合作进程,这种情况就需要用强制锁(Mandatory locking)了。

强制锁的实际代码和上面的完全一样,不过在 Linux 上需要做一些改动:

  1. 挂载文件系统时需要加 mand 参数在文件系统上启用强制锁支持,比较新的 Linux kernel 里已经基本在所有文件系统上都实现了;
  2. 去掉程序的组执行权限;
  3. 增加程序的设置组ID权限。

第二项和第三项在普通情况下实际上是自相矛盾的,所以 Linux 就用这种特殊情况就表示启用强制锁(Mandatory locking)了。

备注

  1. 使用 BSD 的 flock 函数时不能使用强制锁,见 Linux kernel 源码下 mandatory-locking.txt 中的说明:
    Mandatory locks can only be applied via the fcntl()/lockf() locking
    interface - in other words the System V/POSIX interface. BSD style
    locks using flock() never result in a mandatory lock.
  2. 如果一个文件被某进程强制锁住,另一个进程通过 creat 函数或者 open 时加 O_TRUNC 参数,都会导致函数调用失败;
  3. 强制锁在不同的 UNIX 及 类UNIX 系统中实现不同,第二个备注中的 O_TRUNC 参数便是 Linux kernel 和 HP-UX 不同的地方;
  4. tmpfs、nfs 也支持强制锁,可以用这两个文件系统方便测试;
  5. 删除文件(unlink操作)再重新创建文件,会导致强制锁失效;
  6. Linux kernel 的强制锁的实现并不完全可靠(感觉白写了,哈哈,实际情况还要做处理),见 man fcntl 的说明。

强制锁的实际操作:

mount -t tmpfs -o size=10m,mand tmpfs /mnt
cp test /mnt
cd /mnt
chmod g-x test
chmod g+s test

运行 test 程序,同时开另一个终端,直接用 echo 读写 test 文件,会发现强制锁已经起作用。





*