文件锁的原理
理解了文件锁的原理后,就可以理解为什么文件锁可以实现互斥与共享了。
若A进程与B进程同时打开同一个文件,他们使用同一个文件表,使用同一个V节点,V节点指向hello这个文件,里面有一个锁链表,里面记录了锁的信息。
加锁时,进程会检查共享的文件锁链表
- 如果链表上只有读锁节点
- 所有目前其他进程对该文件只加了读锁,由于读锁时共享的,所以不管链表上有几个读节点,当前进程都能成功加读锁。
- 图:
- 提供:链表上可不可以存在n多个读锁节点?
- 答:可以,因为读锁是共享的,不管别的进程有没有解读锁,所有进程都可以加读锁,每加一个读锁,链表上就多一个读锁节点,只有当解锁时节点才被删除。
- 如果链表上有一个写锁节点
- 表明,当前有进程对文件加了锁,锁节点还存在,表示人家目前还没有解锁,写锁是互斥的,所以当前不能加读锁,别人解锁后才能加读锁,加锁后链表上就插入了一个读锁节点。
- 没有解锁就表示数据没有写完,必须等到解锁后才能加读锁
- 提问:链表上可不可能同时存在多个写锁节点?
- 答:不可能,因为写锁是互斥的,目前只有一个进程再给文件加写锁,再解锁之前,别的进程不能加写锁。
- 疑问:链表上会不会同时存在读锁节点和写锁节点?
- 读锁节点和写锁节点也是互斥的,链表上有读锁节点就不可能存在写锁节点,反过来有写锁节点就不能有读锁节点。
如果你想加锁?
- 如果链表上有读锁节点,别人没有解锁,读锁与写锁互斥,不能加写锁。
- 如果链表上有写锁节点,被人没有解锁,写锁与写锁互斥,多以当前进程不能加写锁。
对比进程信号量?
- 进程信号量:进程间共享信号量集合,通过检查集合中信号量的值,从而知道自己能不能操作
- 文件锁:进程共享文件锁链表,通过检查链表上的锁节点,从而知道自己能不能操作。
文件锁其他值得注意的地方
- 在同一进程中,如果多个文件描述符指向同一文件,只要关闭其中任何一个文件描述符,那么该进程加在文件上的文件锁将会被删除,也就是该进程在"文件锁链表"上的“读锁写锁”节点会被删除。
- 进程终止时会关闭所有打开的文件描述符,所以进程结束时会自动删除所有加的文件锁。
- 父进程所加的文件锁,子进程不会继承,我们在讲进程控制时就说过加锁是进程各自私人事情,不能继承,就好比你爸有癌症,你也会有吗?
一个值得理解的问题:多线程之间能不能使用文件锁?
可以,但是线程不能使用同一个open返回的文件描述符,线程必须使用自己open所得到的文件描述符才有效。
代码演示:
线程使用要自己打开文件描述符
void *pth_fun(void *pth_arg)
{
int fd;
fd = open("./hello", O_RDWR|O_CREAT|O_TRUNC, 0664);
if (fd == -1) print_err("./hello", __LINE__, errno);
while(1)
{
SET_WRFLCKW(fd, SEEK_SET, 0, 0);
write(fd, "hello ", 6);
write(fd, "world\n", 6);
SET_UNFLCK(fd, SEEK_SET, 0, 0);
}
return NULL;
}
int main(int argc, char *argv[])
{
int fd = -1;
int ret = -1;
pthread_t tid;
fd = open("./hello", O_RDWR|O_CREAT|O_TRUNC, 0664);
if (fd == -1) print_err("./hello", __LINE__, errno);
ret = pthread_create(&tid, NULL, pth_fun, NULL);
if (ret == -1) print_err("pthread_create fail", __LINE__, ret);
while(1)
{
SET_WRFLCKW(fd, SEEK_SET, 0, 0);
write(fd, "hello ", 6);
write(fd, "world\n", 6);
SET_UNFLCK(fd, SEEK_SET, 0, 0);
}
return 0;
}