文件锁的使用浅析

概述

在多数unix系统中,当多个进程/线程同时编辑一个文件时,该文件的最后状态取决于最后一个写该文件的进程。

对于有些应用程序,如数据库,各个进程需要保证它正在单独地写一个文件。这时就要用到文件锁。

文件锁(也叫记录锁)的作用是,当一个进程读写文件的某部分时,其他进程就无法修改同一文件区域。

能够实现文件锁的函数主要有2个:flockfcntl

早期的伯克利版本只支持flock,该函数只能对整个文件加锁,不能对文件的一部分加锁。

lockf是在fcntl基础上构造的函数,它提供了一个简化的接口。它们允许对文件中任意字节区域加锁,短至一个字节,长至整个文件。


fcntl函数

#include <fcntl.h>

int fcntl(int fd, int cmd, .../*struct flock *flockptr*/);
#返回值:若成功,返回值依赖于cmd,失败返回-1

cmdF_GETLK, F_SETLK, F_SETLKW中的一个。第三个参数是指向flock结构的指针,flock结构如下:

struct flock {
short l_type;/* one of F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence;/* SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start;/* offset in bytes, relative to l_whence */
off_t l_end;/* length, in bytes, 0 means lock to EOF */
off_t l_pid;/* returned with F_GETLK */
};

其中,

  • 锁类型:共享读锁F_RDLCK,独占性写锁F_WRLCK,解锁F_UNLCK
  • 加锁或解锁区域的起始字节偏移量(l_start, l_whence
  • 区域字节长度(L_len
  • 进程的id持有的锁能阻塞当前进程,仅由F_GETLK返回
  • 锁可以在文件尾处开始或者越过尾端开始,但是不能在文件起始位置之前开始
  • l_len=0, 表示锁的范围可以扩大到最大可能偏移量,这意味着不管向文件中追加多少数据,它们都可以处于锁的范围内,而且起始位置可以任意
  • 设置l_startl_whence指向文件的起始位置,并且指定l_len=0,以实现对整个文件加锁(一般l_start=0, l_whence=SEEK_SET

锁的使用

使用锁的基本规则:

  • 任意多个进程在一个给定的字节上可以有一把共享的读锁(F_RDLCK),但是在一个给定的字节上只能有一个进程有一把独占性写锁(F_WRLCK
  • 如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁,如果在一个字节上已经有一把独占性写锁,则不能再对它加任何读锁
  • 对于单个进程而言,如果进程对某个文件区域已经有了一把锁,然后又试图在相同区域再加一把锁,则新锁会替换旧锁
  • 加读锁时,该描述符必须是读打开,加写锁时,该描述符必须是写打开

fcntl三种cmd的使用:

  • F_GETLK:判断由flockptr所描述的锁是否会被另一把锁所排斥(阻塞),如果存在一把锁阻止创建由flockptr所描述的锁,由该现有锁的信息将重写flockptr指向的信息。如果不存在这种情况,则除了将l_type设置为F_UNLCK之处,flockptr所指向结构中的其他信息保持不变
  • F_SETLK:设置由flockptr所描述的锁,如果程序试图获得一把锁,而系统阻止程序获得该锁,则fcntl会立即返回错误,errno设置为EACCES或EAGAIN。当l_type=F_UNLCK时,此命令用来清除指定的锁
  • F_SETLKW:F_SETLK的阻塞版本(wait)。如果程序尝试获得的锁无法被授予,调用进程会进入休眠直到进程获得锁或者信号中断

注意:用F_GETLK 测试能否创建一把锁,然后用F_SETLK尝试建立锁之间并非原子操作,也就是说两次调用之间有可能另一进程插入并创建了相同的锁。如果不希望在等待锁变为可用时产生阻塞,就必须处理由F_SETLK返回的可能出错值

下面是测试一把锁的例子:

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>

pid_t lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
    struct flock lock;
    
    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    l   ock.l_len = len;
    
    if (fcntl(fd, F_GETLK, &lock) < 0) {
        printf("fcntl error: %s\n", strerror(errno));
        return 1;
    }
    
    if (lock.l_type == F_UNLCK) {
        return 0;
    }
    
    return lock.l_pid;
}

锁的继承与释放

锁的继承和释放有以下三条原则:

  • 锁与进程和文件两者相关联。即当一个进程终止时,它所建立的所有锁均释放,对于描述符而言,无论它何时关闭,进程通过它引用的文件上的任何一把锁也都会释放
  • fork产生的子进程不继承父进程所设置的锁
  • 执行exec后,新程序可以继承原程序的锁。注意,如果对一个文件描述符设置了执行时关闭标志,那么当作为exec的一部分关闭该文件描述符时,将释放相应文件的所有锁

避免死锁

如果两个进程互相等待对方持有并且不释放的资源时,这两个进程就会进入死锁状态。

如果一个进程已经控制了文件中的一个加锁区域,然后它又试图对另一个进程控制的区域加锁,那么它就会进入睡眠,并有可能发生死锁。

检测到死锁时,内核必须选择一个进程接收错误返回。


总结

在多进程或多线程环境中,当多个应用需要读写同一个文件时,需要考虑对文件加锁,以保证对文件修改的一致性。

在使用文件锁时,应明确应用模式,防止死锁。

更多关于文件锁的使用细节,请参考《UNIX环境高级编程》。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容