109-记录锁(尾部加锁)

1. 尾部加锁与解锁

在记录锁尾部加锁解锁容易产生大坑。这很隐秘,有必要提一提。

比如下面的代码:

wlock(fd, 0, SEEK_END, 0);
// 追加一字节
write(fd, buf, 1);
unlock(fd, 0, SEEK_END, 0);
// 再次追加一字节
write(fd, buf, 1);

看起来似乎很完美的一段程序,其实是有 bug 的。它运行后的结果是这样的:


这里写图片描述
图1 尾部加锁与解锁后的结果

图 1 中的结果显然不是我们预期的,对于第一次追加完成后,明明解锁了啊?其实不然,当你解锁的时候,此时 SEEK_END 的位置已经变了不是?

2. 实验

程序 lockendfile 对文件 end.txt 加锁后,追加一字节数据,然后解锁。同样这里使用 testlock 程序进行测试。

2.1 代码

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

int unlockend(int fd) {
  puts("unlocking...");
  struct flock flk;
  int err;
  flk.l_type = F_UNLCK;
  flk.l_start = 0;
  flk.l_whence = SEEK_END;
  flk.l_len = 0;
  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("end.txt", O_WRONLY| O_APPEND);
  lockend(fd);
  write(fd, "l", 1); 
  sleep(5);
  unlockend(fd);
  sleep(30);
  close(fd);
  return 0;
}

2.2 编译和运行

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


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

图 1 中左侧程序在 locked 后,向 end.txt 文件追加了一字节数据,在解锁前,使用 testlock 进行测试,发现被加锁。这没毛病。

然后在 unlocked 之后,程序未结束前,再一次 testlock,发现仍然是被加锁的状态,说明这一次 unlocked 并未对那追加的一字节成功解锁。

程序结束后,testlock 测试发现已经没有锁了。

3. 总结

  • 知道尾部加锁可能会产生的问题
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值