编程接口指南

支持的文件系统

下表中列出的文件系统同时支持建议性锁定和强制性锁定。

表 5–4 支持的文件系统

文件系统 

说明 

ufs

缺省的基于磁盘的文件系统 

fifofs

为进程提供对数据的公用访问的命名管道文件的伪文件系统 

namefs

主要由 STREAMS 用来在文件顶部动态挂载文件描述符的伪文件系统 

specfs

提供对特殊字符设备和块设备的访问的伪文件系统 

NFS 只支持建议性文件锁定。procfd 文件系统不支持文件锁定。

打开文件进行锁定

只能使用有效的打开描述符来请求对文件进行锁定。对于读取锁定,文件必须至少是使用读取权限打开的。对于写入锁定,文件也必须是使用写入权限打开的。在以下示例中,将打开文件以进行读取和写入访问。

...

 	filename = argv[1];

 	fd = open (filename, O_RDWR);

 	if (fd < 0) {

 		perror(filename);

 		exit(2);

 	}

 	...

设置文件锁定

要锁定整个文件,请将偏移量设置为零并将大小设置为零。

可以使用多种方法在文件上设置锁定。方法的选择取决于锁定如何与程序的其余部分进行交互、性能以及可移植性。本示例使用 POSIX 标准兼容的 fcntl(2) 接口。此接口尝试锁定文件,直到发生以下情况之一:

设置和删除记录锁定

锁定记录时,请不要将锁定段的起始点和长度设置为零。否则,此锁定过程与文件锁定相同。

使用记录锁定的原因是存在数据争用。因此,当无法获取所有所需锁定时,应收到失败响应:

本示例说明使用 fcntl(2) 锁定的记录。

{

 	struct flock lck;

   	...

 	lck.l_type = F_WRLCK;	/* setting a write lock */

 	lck.l_whence = 0;	/* offset l_start from beginning of file */

 	lck.l_start = here;

 	lck.l_len = sizeof(struct record);



 	/* lock "this" with write lock */

 	lck.l_start = this;

 	if (fcntl(fd, F_SETLKW, &lck) < 0) {

 		/* "this" lock failed. */

 		return (-1);

 ...

}

下一示例说明 lockf(3C) 接口。

#include <unistd.h>



{

 ...

 	/* lock "this" */

 	(void) lseek(fd, this, SEEK_SET);

 	if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {

 		/* Lock on "this" failed. Clear lock on "here". */

 		(void) lseek(fd, here, 0);

 		(void) lockf(fd, F_ULOCK, sizeof(struct record));

 		return (-1);

}

 

应使用与设置锁定相同的方法来删除锁定。只是锁定类型有所不同 (F_ULOCK)。解除锁定不受其他进程的阻止,并且只影响调用进程所设置的锁定。解除锁定只影响在先前锁定调用中指定的文件段。

获取锁定信息

可以确定哪个进程在持有锁定。可以按照前面示例所示设置锁定,并在 fcntl(2) 中使用 F_GETLK

下一示例查找并列显文件中所有锁定段上的标识数据。


示例 5–2 列显文件的锁定段

struct flock lck;



 	lck.l_whence = 0;

 	lck.l_start = 0L;

 	lck.l_len = 0L;

 	do {

 		lck.l_type = F_WRLCK;

 		(void) fcntl(fd, F_GETLK, &lck);

 		if (lck.l_type != F_UNLCK) {

 			(void) printf("%d %d %c %8ld %8ld\n", lck.l_sysid, lck.l_pid,

            (lck.l_type == F_WRLCK) ? 'W' : 'R', lck.l_start, lck.l_len);

 			/* If this lock goes to the end of the address space, no

 			 * need to look further, so break out. */

 			if (lck.l_len == 0) {

 			/* else, look for new lock after the one just found. */

 					lck.l_start += lck.l_len;

 			}

 		}

 	} while (lck.l_type != F_UNLCK);

fcntl(2)F_GETLK 命令可以在等待服务器响应时处于休眠状态。如果客户机或服务器出现资源不足的情况,则此命令可能会失败,同时返回 ENOLCK

lockf(3C)F_TEST 命令一起使用来测试进程是否在持有锁定。此接口并不返回有关锁定的位置或拥有权的信息。


示例 5–3 使用 lockf 测试进程

(void) lseek(fd, 0, 0L);

 /* set the size of the test region to zero (0). to test until the

    end of the file address space. */

 if (lockf(fd, (off_t)0, SEEK_SET) < 0) {

 	switch (errno) {

 		case EACCES:

 		case EAGAIN:

 			(void) printf("file is locked by another process\n");

 			break;

 		case EBADF:

 			/* bad argument passed to lockf */

 			perror("lockf");

 			break;

 		default:

 			(void) printf("lockf: unexpected error <%d>\n", errno);

 			break;

 	}

 

进程派生和锁定

进程派生时,子进程会得到父进程打开的文件描述符的副本。子进程不继承锁定,因为锁定由特定进程拥有。父进程和子进程共享每个文件的公用文件指针。并且都可以尝试在同一文件中的相同位置设置锁定。使用 lockf(3C)fcntl(2) 都会出现此问题。如果持有记录锁定的程序进行派生,则子进程应该关闭此文件。关闭此文件之后,子进程应该重新打开此文件以设置新的独立文件指针。

死锁处理

UNIX 锁定功能可以检测和避免死锁。仅当系统准备将记录锁定接口置于休眠状态时才会发生死锁。执行搜索来确定两个进程是否处于死锁状态。如果检测到潜在的死锁,则锁定接口会失败,并设置 errno 来指示死锁。使用 F_SETLK 设置锁定的进程不会导致死锁,因为当不能被授予锁定时,这些进程并不会等待。