1、概述
记录锁是读写锁的一种扩展类型,它可用于有亲缘关系或无亲缘关系的进程之间共享某个文件的读与写。被锁住的文件通过其描述符访问,执行上锁操作的函数是fcntl。这种类型的锁通常在内核中维护,其属主是由属主的进程ID标识的。这意味着这些锁用于不同进程间的上锁,而不是用于同一进程内不同线程间的上锁。
2、Posix fcntl 记录上锁
记录上锁的Posix接口是 fcntl 函数。
#include <fcntl.h>
int fcntl(int fd, int cmd, .../* struct flock *arg */);
//返回:若成功则取决于cmd,若出错则为-1
用于记录上锁的cmd参数共有三个值。这三个命令要求第三个参数arg是指向某个flock结构的指针:
struct flock {
short l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /* SEEK_SET, SEEK_CUR, SEEKEND */
off_t l_start; /* relative starting offset in bytes */
offt l_len; /* #bytes; 0 means until end-of-file */
pid_t l_pid; /* PID returned by F_GErLK /
};
这三个命令如下:
F_SETLK 获取(l_type成员为F_RDLCK或F_WRICK)或释放(l_type成员为F_UNLCK)由arg指向的flock结构所描述的锁。如果无法将该锁授予调用进程,该函数就立即返回一个EACCES或EAGAIN错误而不阻塞。
F_SETLKW 该命令与上一个命令类似,不过如果无法将所请求的锁授予调用进程,调用线程将阻塞到该锁能够授予为止。(该命令的名字中最后一个字母w意思是“等待(wait)".)
F_GETLK 检查由arg指向的锁以确定是否有某个已存在的锁会妨碍将新锁授予调用进程。
flock结构描述锁的类型(读出锁或写入锁)以及待锁住的字节范围。跟lseek一样,起始字节偏移是作为一个相对偏移(l_start成员)伴随其解释(l_whence成员)指定的。l_whence成员有以下三个取值。
- SEEKSET: l_start 相对于文件的开头解释。
- SEEK-CUR: l_start 相对于文件的当前字节偏移(即当前读写指针位置)解释。
- EEKEND: l_start 相对于文件的末尾解释。
l_len成员指定从该偏移开始的连续字节数。长度为0意思是“从起始偏移到文件偏移的最大可能值”。因此,锁住整个文件有两种方式。
(1)指定 l_whence 成员为 SEEK_SET, l_start 成员为0, l_len成员为0。
(2)使用 lseek 把读写指针定位到文件头,然后指定 l_whence 成员为SEEK_CUR,l_start 成员为0,l_len成员为0。
3、小结
- 与普通的互斥相比,当被保护数据的读访问比写访问更为频繁时,读写锁能提供更高的并发度。
- 读写锁可以只通过使用互斥锁和条件变量来实现,
- 线程可能在阻塞于pthread_cond_wait调用期间被取消,我们的读写锁实现允许看到这种情况的发生。我们使用线程取消清理处理程序解决了这个问题。