记录锁相当于线程同步中读写锁的一种扩展类型,可以用来对有亲缘或无亲缘关系的进程进行文件读与写的同步,通过fcntl函数来执行上锁操作。尽管读写锁也可以通过在共享内存区来进行进程的同步,但是fcntl记录上锁往往更容易使用,且效率更高。
记录锁的功能:当一个进程正在读或修改文件的某个部分是,它可以阻止其他进程修改同一文件区。对于这个功能阐述我认为有三点要解释的:
- 记录锁不仅仅可以用来同步不同进程对同一文件的操作,还可以通过对同一文件加记录锁,来同步不同进程对某一共享资源的访问,如共享内存,I/O设备。
- 对于劝告性上锁(POSIX定义的记录锁即是这种类型的锁),当一个进程通过上锁对文件进行操作时,它不能阻止另一个非协作进程对该文件的修改。
- 即使是强制性上锁(有些系统定义),也不能完全保证该文件不会被另一个进程修改。因为强制性锁对unlink函数没有影响,所以一个进程可以先删除该文件,然后再将修改后的内容保存为同一文件来实现修改。具体可参考《APUE》P367 。
1记录锁函数接口
记录上锁的POSIX接口函数fcntl如下:
/* Do the file control operation described by CMD on FD.
The remaining arguments are interpreted depending on CMD. */
int fcntl (int __fd, int __cmd, ...);
//根据cmd的不同有以下三种类型的调用
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
由函数名称可知fcntl的功能是对文件的控制操作,根据传入不同的操作类型命令cmd,fcntl会执行不同的操作,fcnt根据cmd不同,接收可变的参数。具体有以下五种类型的操作:
/*
cmd = F_DUPFD,复制一个文件描述符;
*/
int fcntl(int fd, int cmd);
/*
cmd = F_GETFD,获得文件描述符标志;
cmd = F_SETFD,设置文件描述符标志;arg = 描述符标志的值,目前只定义了一个标志: FD_CLOEXEC
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
*/
/*
cmd = F_GETFL,获得文件状态标志;
cmd = F_SETFL,设置文件状态标志;arg = 状态标志的值
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
*/
/*
cmd = F_GETOWN,获得当前接收SIGIO和SIGURG信号的进程ID或进程组ID
cmd = F_SETOWN,设置接收SIGIO和SIGURG信号的进程ID或进程组ID;arg = 进程ID或进程组ID
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
*/
/*
Return value:
对于成功的调用,根据操作类型cmd不同,有以下几种情况:
F_DUPFD 返回新的文件描述符
F_GETFD 返回文件描述符标志
F_GETFL 返回文件状态标志
F_GETOWN 进程ID或进程组ID
All other commands 返回0
调用失败, 返回-1,并设置errno。
*/
上面四个功能都是fcntl提供的很常用的操作,关于记录锁的功能就是fcntl提供的第五个功能,具体使用如下:
int fcntl(int fd, int cmd, struct flock *lock);
/*
cmd = F_GETLK,测试能否建立一把锁
cmd = F_SETLK,设置锁
cmd = F_SETLKW, 阻塞设置一把锁
*/
//POSIX只定义fock结构中必须有以下的数据成员,具体实现可以增加
struct flock {
short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /* 加锁的起始位置:SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 加锁的起始偏移,相对于l_whence */
off_t l_len; /* 上锁的字节数,如果为0,表示从偏移处一直到文件的末尾*/
pid_t l_pid; /* 已经占用锁的PID(只对F_