110-建议性锁和强制性锁

来源:互联网 发布:python主要用来做什么 编辑:程序博客网 时间:2024/04/28 15:21

这两个名词非常陌生,如果你直接看 apue,可能会懵圈,不知道所云。没事,你已经找到了这一篇文章,相信你能学会。

1. 提出问题

假设有一个文件 b.txt,进程 A 对其进行了加锁。另外,还有一个进程 B,如果它打开了文件 b.txt,一上来就直接读写 b.txt,会不会出问题?

一般来说,如果进程 A 和进程 B 都是你自己写的,你肯定会遵守规则,两个进程你都会加上加锁和解锁的代码。

但是,如果进程 A 是你写的,进程 B 是另一个陌生人写的,它并不知道进程 A 会给 b.txt 加锁。索性就直接读写吧。

2. 建议性锁和强制性锁

  • 建议性锁(Advisory Locking,注意这是形容词)

所谓的建议性锁,并不是一把真正的锁,而是文件系统本身具备的性质,一种属性。所以英文术语使用的是 Advisory Locking 而不是 Advisory Lock. 在中文里,真的是找不到对应的名词。。。

对遵守规则的人来说的。比方刚刚说进程 A 和进程 B 都是你自己的作品,你肯定是按规则办事的,如果进程 B 不是你写的,人家也不按规则办事,你完全没办法,即使再没有获得锁的情况下进程 B 也可以自由读写文件 b.txt.

  • 强制性锁(Mandatory Locking)

如果文件系统开启了强制性锁属性,同时文件也开启了强制性锁机制,那么进程 B 试图在不获得锁的情况读写 b.txt,要么阻塞,要么返回 EAGAIN(看描述符是阻塞的还是非阻塞的).

根据上面的叙述,可以举个例子:

比如路口的红绿灯就是一种建议性设置,遵守还是不遵守完全在于个人。如果红绿灯是强制性的,就不在于个人意愿了,红灯情况下,强制把你拦住。

3. 打开强制性锁属性

3.1 打开文件强制性锁属性

默认情况下,文件系统的 Mandatory Locking 属性是关闭的,需要使用 mount -o mand 命令打开。

比如:

mount -o remount,mand /dev/sda1 /

表示重新将硬盘 /dev/sda1 以 Mandatory Locking 方式挂载到根文件系统。

如果你不知道你当前的文件系统上挂载的是哪个设备,可以通过 df -h 查看。

3.2 打开文件本身的强制性锁属性

即使你打开了文件系统的 Mandatory Locking 属性,你不打开文件本身的 Mandatory Locking 属性也是不行的。

有两种方法可以打开文件的 Mandatory Locking 属性:

  • 打开文件前,给文件加上Mandatory Locking 属性。可以使用下面的命令:
chmod g+s b.txtchmod g-x b.txt

说的通俗点,就是去掉组执行属性,加上设置组 ID 属性。这种事情也完全可以在程序里去做,也就是第二种办法:

  • 打开文件后,使用 fchmod 函数修改 x 和 s 属性:
struct stat statbuf;fstat(fd, &statbuf);fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID);

千万不要问为什么这样做就可打开 Mandatory Locking 属性,这是设计者制定的规则,可能这看起来毫无根据……

4. 实验

程序 readfile 尝试在对文件加锁的情况下读取数据,该程序需要从命令行传入数,表示读取哪个文件。

./readfile <filename>

readfile 会在读取文件内容前对锁进行测试。

4.1 代码

// readfile.c#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#define PERR(err, msg) do { errno = err; perror(msg); exit(-1); } while(0);int testlock(int fd, int start, int len) {  struct flock flk;  int err;  flk.l_type = F_WRLCK;  flk.l_start = start;  flk.l_whence = SEEK_SET;  flk.l_len = len;  err = fcntl(fd, F_GETLK, &flk);  if (err < 0) PERR(errno, "getlock");  if (flk.l_type == F_UNLCK) return 0;  return flk.l_pid;}int main(int argc, char *argv[]) {  char *filename = argv[1];  char buf[64] = { 0 };  int err;  int fd = open(filename, O_RDONLY | O_NONBLOCK);  if (testlock(fd, 0, 0) != 0)      printf("%s is locked\n", filename);  err = read(fd, buf, 2);   if (err < 0)      printf("errno = %d, msg = %s\n", errno, strerror(errno));  else    printf("content: %s\n", buf);  close(fd);  return 0;}

4.2 编译

$ gcc readfile.c -o readfile

4.3 运行

  • 测试一

不开启 Mandatory Locking 属性。


这里写图片描述
图1 不开启 Mandatory Locking 属性时的运行结果

  • 测试二

开启 Mandatory Locking 属性。


这里写图片描述
图2 开启 Mandatory Locking 属性时的运行结果

可以看到,在 Mandatory Locking 开启的情况下, readfile 进程尝试以 NONBLOCK 方式读取数据时产生错误。errno = 11 在这里实际上就是 EAGAIN.

5. 总结

  • 理解 Advisory Locking 和 Mandatory Locking 的作用
  • 掌握打开 Mandatory Locking 的方法
0 0
原创粉丝点击