|
,然后返回;否则,就将 lock 指向的 flock 结构中的 l_type 设置为 F_UNLCK,并保持 flock 结构中其他信息不变返回,而不会对该文件真正加锁。 F_SETLK:进程用它来对文件的某个区域进行加锁(l_type的值为 F_RDLCK 或 F_WRLCK)或者删除锁(l_type 的值为F_UNLCK),如果有其他锁阻止该锁被建立,那么 fcntl() 就出错返回 F_SETLKW:与 F_SETLK 类似,唯一不同的是,如果有其他锁阻止该锁被建立,则调用进程进入睡眠状态,等待该锁释放。一旦这个调用开始了等待,就只有在能够进行加锁或者收到信号时才会返回 需要注意的是,F_GETLK 用于测试是否可以加锁,在 F_GETLK 测试可以加锁之后,F_SETLK 和 F_SETLKW 就会企图建立一把锁,但是这两者之间并不是一个原子操作,也就是说,在 F_SETLK 或者 F_SETLKW 还没有成功加锁之前,另外一个进程就有可能已经插进来加上了一把锁。而且,F_SETLKW 有可能导致程序长时间睡眠。还有,程序对某个文件拥有的各种锁会在相应的文件描述符被关闭时自动清除,程序运行结束后,其所加的各种锁也会自动清除。 fcntl() 既可以用于劝告锁,也可以用于强制锁,在默认情况下,它用于劝告锁。如果它用于强制锁,当进程对某个文件进行了读或写这样的系统调用时,系统则会检查该文 件的锁的 O_NONBLOCK 标识,该标识是文件状态标识的一种,如果设置文件状态标识的时候设置了 O_NONBLOCK,则该进程会出错返回;否则,该进程被阻塞。cmd 参数的值 F_SETFL 可以用于设置文件状态标识。 此外,系统调用 fcntl() 还可以用于租借锁,此时采用的函数原型如下: int fcntl(int fd, int cmd, long arg); 与租借锁相关的 cmd 参数的取值有两种:F_SETLEASE 和 F_GETLEASE。其含义如下所示: F_SETLEASE:根据下面所描述的 arg 参数指定的值来建立或者删除租约: F_RDLCK:设置读租约。当文件被另一个进程以写的方式打开时,拥有该租约的当前进程会收到通知 F_WRLCK:设置写租约。当文件被另一个进程以读或者写的方式打开时,拥有该租约的当前进程会收到通知 F_UNLCK:删除以前建立的租约 F_GETLEASE:表明调用进程拥有文件上哪种类型的锁,这需要通过返回值来确定,返回值有三种:F_RDLCK、F_WRLCK和F_UNLCK,分别表明调用进程对文件拥有读租借、写租借或者根本没有租借 某个进程可能会对文件执行其他一些系统调用(比如 OPEN() 或者 TRUNCATE()),如果这些系统调用与该文件上由 F_SETLEASE 所设置的租借锁相冲突,内核就会阻塞这个系统调用;同时,内核会给拥有这个租借锁的进程发信号,告知此事。拥有此租借锁的进程会对该信号进行反馈,它可能 会删除这个租借锁,也可能会减短这个租借锁的租约,从而可以使得该文件可以被其他进程所访问。如果拥有租借锁的进程不能在给定时间内完成上述操作,那么系 统会强制帮它完成。通过 F_SETLEASE 命令将 arg 参数指定为 F_UNLCK 就可以删除这个租借锁。不管对该租借锁减短租约或者干脆删除的操作是进程自愿的还是内核强迫的,只要被阻塞的系统调用还没有被发出该调用的进程解除阻塞, 那么系统就会允许这个系统调用执行。即使被阻塞的系统调用因为某些原因被解除阻塞,但是上面对租借锁减短租约或者删除这个过程还是会执行的。 需要注意的是,租借锁也只能对整个文件生效,而无法实现记录级的加锁。 回页首 文件锁的使用样例 为了使读者更深入理解本文中介绍的内容,下面我们给出了一个例子来详细介绍文件锁的具体用法。这个例子可以用来检测所使用的文件是否支持强制锁,其源代码如下所示: 清单 5. 锁的使用方法具体示例 # cat -n mandlock.c 1 #include <errno.h> 2 #include <stdio.h> 3 #include <fcntl.h> 4 #include <sys/wait.h> 5 #include <sys/stat.h> 6 7 int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) 8 { 9 struct flock lock; 10 11 lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */ 12 lock.l_start = offset; /* byte offset, relative to l_whence */ 13 lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ 14 lock.l_len = len; /* #bytes (0 means to EOF) */ 15 16 return( fcntl(fd, cmd, &lock) ); 17 } 18 19 #define read_lock(fd, offset, whence, len) \ 20 lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len) 21 #define write_lock(fd, offset, whence, len) \ 22 lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len) 23 24 #define err_sys(x) { perror(x); exit(1); } 25 26 int main(int argc, char *argv[]) 27 { 28 int fd, val; 29 pid_t pid; 30 char buf[5]; 31 struct stat statbuf; 32 if (argc != 2) { 33 fprintf(stderr, "usage: %s filename\n", argv[0]); 34 exit(1); 35 } 36 if ((fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC )) < 0) 37 err_sys("open error"); 38 if (write(fd, "hello world", 11) != 11) 39 err_sys("write error"); 40 41 /* turn on set-group-ID and turn off group-execute */ 42 if (fstat(fd, &statbuf) < 0) 43 err_sys("fstat error"); 44 if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0) 45 err_sys("fchmod error"); 46 47 sleep(2); 48 49 if ((pid = fork()) < 0) { 50 err_sys("fork error"); 51 } else if (pid > 0) { /* parent */ 52 /* write lock entire file */ 53 if (write_lock(fd, 0, SEEK_SET, 0) < 0) 54 err_sys("write_lock error"); 55 56 sleep(20); /* wait for chil |
|