Linux系统编程—记录锁

1. 简介

使用 fcntl() 能够在一个文件的任意一个部分上放置一把锁,这个文件部分既可以是一个字节范围,也可以是整个文件。

2. API

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

使用 fcntl() 操作记录锁时,cmd 可以取值如下:

  • F_SETLK:设置锁,可以是加锁或解锁,取决于 arg 参数;如果无法立马加锁,则 fcntl() 会立马返回 -1,并将 errno 设为 EAGAINEACCES,而不阻塞。
  • F_SETLKW:与 F_SETLK 类似,但当无法立马加锁时,此操作会导致 fcntl() 阻塞。

arg 参数是一个指向 flock 结构体类型的指针:

struct flock {
    ...
    short l_type;    /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */
    short l_whence;  /* How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END */
    off_t l_start;   /* Starting offset for lock */
    off_t l_len;     /* Number of bytes to lock */
    pid_t l_pid;     /* PID of process blocking our lock (set by F_GETLK and F_OFD_GETLK) */
    ...
};
  • l_type 的取值可以为 F_RDLCK, F_WRLCK, F_UNLCK,分别表示加读锁、加写锁、解锁。

  • l_whence 用于指定如何解释锁的起始偏移量 l_start,其含义与 lseek() 相同;l_len 指定了加锁范围的长度。

  • l_whence 设为 SEEK_SET,并将 l_startl_len 都设为 0,可以锁住整个文件。

  • 解锁一个没有被锁住的区域不会导致出错。

  • 对于一个区域,一个进程只能同时持有一种类型的锁;如果在该区域上放置一把类型不同的锁,则会原子地将锁类型转换为新类型。

  • 在已有锁的区域中间放置一把类型不同的锁会导致产生三把锁,即分裂了原先被锁住的区域,区域的两端是旧类型的锁,中间是新类型的锁。

  • 在紧挨着已有锁的区域放置一把类型相同的锁会导致锁合并,即该类型的锁(此时只有一把)会锁住之前的区域和紧挨着的新区域。

  • fork() 创建的子进程不会继承父进程的记录锁。

  • 记录锁会在 exec() 后得到保留,除非设置了 close-on-exec 标志。

  • 一个进程中的所有线程会共享同一组记录锁。

  • 记录锁同时与一个进程和一个 i-node 关联;当进程终止之后,该进程持有的所有记录锁将会被释放;此外,当进程关闭了一个文件描述符后,该进程持有的对应文件上的所有记录锁会被释放掉,而不管这些锁是通过哪个文件描述符获得的(如,通过不同的 open() 调用或 dup() 调用)。

  • 每个打开的文件都关联着一个链表,链表中保存着该文件上的锁。链表中的锁会先按照进程 ID 再按照起始偏移量来排序,如下图所示。所以,添加或删除一个锁所需要的时间与该文件上已有的锁的数量之间大概是一个线性关系。

在这里插入图片描述

3. 例子

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

const char* FILENAME = "test.txt";

void test_record_lock() {
    struct flock lock;

    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 4;

    int fd = open(FILENAME, O_RDWR);
    fcntl(fd, F_SETLKW, &lock);

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork()");
        exit(EXIT_FAILURE);
    }
    if (pid == 0) {
        close(fd);
        fd = open(FILENAME, O_RDWR);
        lock.l_type = F_RDLCK;
        fcntl(fd, F_SETLKW, &lock);

        char buf[5] = {0};
        read(fd, buf, sizeof(buf)-1);
        std::cout << buf << '\n';

        close(fd);
        _exit(EXIT_SUCCESS);
    }

    write(fd, "ABCD", 4);

    lock.l_type = F_UNLCK;
    fcntl(fd, F_SETLKW, &lock);

    close(fd);
    wait(NULL);
}

int main() {
    test_record_lock();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值