105-记录锁(基础)

在上一文简单的了解了下什么是记录锁以及掌握它所必备的前修知识。本文重点探讨如果使用 fcntl 对文件部分字节加锁。

1. 加锁解锁方法

1.1 加锁

void wlock(int fd, int start, int len) {
  struct flock flk;

  // 加写锁,如果是加读锁,这里就写 F_RDLCK
  flk.l_type = F_WRLCK; 

  // 设置参考点,参考 lseek;这里表示参考点为第 0 字节处。
  flk.l_whence = SEEK_SET; 

  // 相对于参考点什么位置;
  flk.l_start = start; 

  // 要加锁的字节数。
  flk.l_len = len; 

  // 获得锁。如果该区域有任何一个字节被其他进程加锁,则阻塞。如果设置 cmd 参数为 F_SETLK,获得所失败会返回 -1,同时 errno 置为 EAGAIN,成功返回 0.
  fcntl(fd, F_SETLKW, &flk); 
}

关于加锁有几点与读写锁有所不同:

  • 同一个进程重复加锁不会阻塞,已最后一次加的锁为准
  • 加锁范围
    • 如果 len > 0, [whence+start,whence+start+len)
    • 如果 len = 0, [whence+start,)
  • 欲加锁的范围中,有任何一个字节被加锁,则等待

1.2 解锁

void wlock(int fd, int start, int len) {
  struct flock flk;

  // 解锁
  flk.l_type = F_UNLCK; 
  flk.l_whence = SEEK_SET; 
  flk.l_start = start; 

  // 要解锁的字节数。
  flk.l_len = len; 
  fcntl(fd, F_SETLKW, &flk); 
}

解锁的程序与加锁区别就在于 flock 的 type 字段。

接下来,我们用实验来观察记录锁是如何互斥的。

2. 实验

程序 lockfile 用来对文件进行加写锁,需要从命令行传参数,格式如下:

./lockfile <filename> <start> <len>
  • filename 表示想对哪个文件加锁
  • start 表示从第几个字节开始
  • len 表示加锁多少字节

2.1 代码

// lockfile.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, int start, int len) {
  puts("locking...");
  struct flock flk;
  int err;
  flk.l_type = F_WRLCK;
  flk.l_whence = SEEK_SET;
  flk.l_start = start;
  flk.l_len = len;
  err = fcntl(fd, F_SETLKW, &flk);
  if (err < 0) PERR(errno, "lock");
  puts("locked...");
  return err;
}

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

int main(int argc, char* argv[]) {
  if (argc < 4) {
    printf("Usage: %s <filename> <start> <len>\n", argv[0]);
    return -1;
  }
  char *filename = argv[1];
  int start = atoi(argv[2]);
  int len = atoi(argv[3]);
  printf("pid: %d\n", getpid());
  int fd = open(filename, O_WRONLY);
  lock(fd, start, len);
  sleep(10);
  unlock(fd, start, len);
  sleep(10);
  return 0;
}

2.2 编译

$ gcc lockfile.c -o lockfile

2.3 运行

测试文件 a.txt 内容(11 字节):

hello world

可以通过命令 echo -n 'hello world' > a.txt 获得 a.txt.

  • 测试一

进程 A 和 B 对 [2, 5) 范围加锁。


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

  • 测试二

进程 A 对 [2, 3) 范围加锁,进程 B 对 [2, 5) 范围加锁。


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

  • 测试三

进程 A 对 [2, 3) 范围加锁,进程 B 对 [3, 5) 范围加锁。


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

  • 测试四

进程 A 对整个文件加锁,进程 B 对 [3, 5) 范围加锁。


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

3. 总结

  • 掌握用 fcntl 加锁解锁的方法
  • 注意 flock 结构的 l_len 字段

练习:运行本文中的实验,观察结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值