108-记录锁(继承与释放)

1. 释放

  • 如果进程关闭,所有的记录锁都被释放
  • 任何一个描述符关闭,该描述符引用的文件上的任何记录都被释放。

有关上面两点,实际上在上一篇文章就已经解释过了,对于同一个文件来说,无论你是 open 还是 dup 多少次,vnode 节点始终只有一份,而锁的信息是在 vnode 节点中保存。

2. 继承

  • fork 产生的子进程不继承父进程所设置的锁。

这种约束是有依据的:因为记录锁的作用就是阻止多个进程同时写同一个文件。如果 fork 继承了父进程的锁,那么父进程和子进程相当于同时获得了锁,这是错误的。

  • 执行 exec 系列函数后,如果描述符没有设置 O_CLOEXEC(执行时关闭),新程序仍然可以使用原来的锁。

3. 实验

程序 closelock 对文件 test.txt 的 [2,4) 字节进行加写锁,然后 dup 了一份新的描述符,然后立即 close,此时对文件锁进行测试,看是否能够获得写锁。

3.1 代码

// closelock.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define PERR(err, msg) do { errno = err; perror(msg); exit(-1); } while(0)

int lock(int fd) {
  puts("locking...");
  struct flock flk;
  int err;
  flk.l_type = F_WRLCK;
  flk.l_start = SEEK_SET;
  flk.l_whence = 2;
  flk.l_len = 2;
  err = fcntl(fd, F_SETLKW, &flk);
  if (err < 0) PERR(errno, "lock");
  puts("locked...");
  return err;
}

int unlock(int fd) {
  puts("unlocking...");
  struct flock flk;
  int err;
  flk.l_type = F_UNLCK;
  flk.l_start = SEEK_SET;
  flk.l_whence = 2;
  flk.l_len = 2;
  err = fcntl(fd, F_SETLKW, &flk);
  if (err < 0) PERR(errno, "unlock");
  puts("unlocked...");
  return err;
}

int main() {
  printf("pid: %d\n", getpid());
  int fd = open("test.txt", O_WRONLY);
  lock(fd);
  sleep(10);
  puts("close fd2");
  int fd2 = dup(fd);
  close(fd2);
  sleep(10);
  unlock(fd);
  close(fd);
  return 0;
}

3.2 编译和运行

  • 编译
$ gcc closelock.c -o closelock
  • 运行


这里写图片描述
图1 运行结果

图 1 中,左侧是上面的代码运行的结果,右侧使用我们之前写的 testlock 程序《记录锁(测试命令)》 进行测试是否能加写锁。第一次运行是在 close fd2 前进行,第二次是在 close fd2 后运行的结果。

可以看到,第二次测试结果是 0,表示该文件已经没有锁了,尽管 closelock 此时还没有执行解锁函数。

4. 总结

  • 关闭描述符会关闭锁,无论该文件描述符被 dup 几次或者文件被打开几次
  • 子进程不继承锁状态
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页