当多个进程在编辑同一个文件时,在 UNIX 系统中,文件的最后状态取决于写该文件的最后一个进程,但是进程必须要确保它正在单独写一个文件,所以需要用到记录锁机制。记录锁的功能:当一个进程在读或修改文件的某一部分时,它可以阻止其他进程修改同一个文件区,记录锁也称为字节范围锁,因为它锁定的只是文件中的一个区域或整个文件。
记录锁可以通过 fcntl 函数进行控制,该函数的基本形式如下:
/* fcntl记录锁 */ /* * 函数功能:记录锁; * 返回值:若成功则依赖于cmd的值,若出错则返回-1; * 函数原型: */ #include <fcntl.h> int fcntl(int fd, int cmd, .../* struct flock *flockptr */); /* * 说明: * cmd的取值可以如下: * F_GETLK 获取文件锁 * F_SETLK、F_SETLKW 设置文件锁 * 第三个参数flockptr是一个结构指针,如下: */ struct flock { short l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */ off_t l_start; /* offset in bytes, relative to l_whence */ short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ off_t l_len; /* length, in bytes; 0 means lock to EOF */ pid_t l_pid; /* returned with F_GETLK */ };
共享读锁和独占性写锁的基本规则是:多个进程在一个给定的字节上可以有一把共享读锁,但是在一个给定的字节上只能有一个进程独用一把写锁。若在一个给定的字节上已经有一把或多把读锁,则不能在该字节上再加上写锁;若在一个给定的字节上已经有一把独占性的写锁,则不能再对其加任何的读锁。这些规则如下表所示:
上面的兼容性规则适用于不同进程提出的请求,并不适用于单个进程提出的多个锁请求;若是一个进程对一个文件区间已经加上一把锁,后来该进程又企图在同一个文件取件加上另一把锁,那么新锁会替换掉老锁。
加读锁时文件描述符必须是以读打开的,加写锁时文件描述符必须是写打开的。以下是 fcntl 函数的三种命令:
关于记录锁的自动继承和释放有以下三条规则:
#include <fcntl.h> #include "apue.h" int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len); int lock_test(int fd, int type, off_t offset, int whence, off_t len); int main(void) { int fd, tmp; pid_t pid; pid = getpid(); printf("pid: %d\n", pid); fd = open("lock.txt", O_RDWR); if(fd < 0) err_sys("open file error"); tmp = lock_reg(fd, F_SETLK, F_WRLCK, 4, SEEK_SET, 1); if(tmp < 0) err_sys("F_SETLK error"); else printf("F_SETLK success\n"); sleep(3); tmp = lock_test(fd, F_WRLCK, 3, SEEK_SET, 2); if(tmp == 0) printf("not lock\n"); else printf("locked\n"); close(fd); exit(0); } int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) { struct flock lock; lock.l_len = len; lock.l_start = offset; lock.l_type = type; lock.l_whence = whence; return(fcntl(fd, cmd, &lock)); } int lock_test(int fd, int type, off_t offset, int whence, off_t len) { struct flock lock; lock.l_len = len; lock.l_start = offset; lock.l_type = type; lock.l_whence = whence; if(fcntl(fd, F_GETLK, &lock) < 0) err_sys("fcntl error"); if(lock.l_type == F_UNLCK) return(0); return(lock.l_pid); }