oslo_concurrency模块文件锁功能分析

[toc]
oslo_concurrency模块可以对OpenStack中的代码块提供线程锁,以及进程锁。由于多线程在Python中使用场景不多,因此不讨论oslo_concurrency的线程锁,主要讨论oslo_concurrency进程锁的实现细节。

一、oslo_concurrency的进程锁

oslo_concurrency的进程锁基于fasteners模块,而fasteners模块中通过linux的内核接口函数lockf(...)对文件进行加锁。

# oslo_concurrency的进程锁接口
def external_lock(name, lock_file_prefix=None, lock_path=None):
    lock_file_path = _get_lock_path(name, lock_file_prefix, lock_path)
    # InterProcessLock来自fasteners模块,lock_file_path为需要加锁的文件地址
    return InterProcessLock(lock_file_path)
    
# fasteners模块中的InterProcessLock类
# 为了实现操作系统的通用性,windows系统通过msvcrt对文件加锁
if os.name == 'nt':
    import msvcrt
    InterProcessLock = _WindowsLock
else:
    import fcntl
    InterProcessLock = _FcntlLock
    
class _FcntlLock(_InterProcessLock):
    """Interprocess lock implementation that works on posix systems."""

    # 通过lockf可以实现以下操作
    # LOCK_SH:共享锁
    # LOCK_EX:排他锁
    # LOCK_NB:非阻塞锁(可以和LOCK_SH、LOCK_EX一起使用)
    # LOCK_UN:释放锁
    def trylock(self):
        fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)

    def unlock(self):
        fcntl.lockf(self.lockfile, fcntl.LOCK_UN)

从oslo_concurrency源码分析,oslo_concurrency进程锁最终调用fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)实现,在他传入的参数中LOCK_EX表示独占锁,LOCK_NB表示无论是否请求到锁,函数立即返回。

二、Linux中的文件锁

oslo_concurrency模块通过fcntl模块实现锁功能。实际上是fcntl模块一个接口代理模块,fcntl.py中没有定义任何代码逻辑,而是调用了/usr/lib64/python2.7/lib-dynload/fcntlmodule.so目录下的C语言接口,即Linux的文件锁接口函数。
在Linux中有三个文件锁接口函数flock(...)、fcntl(...)、lockf(...),这三个接口可以创建Linux中不同类型的锁。下面简单介绍一下Linux中不同类型的锁
Linux中支持四种功能的文件锁:劝告锁、强制锁、租赁锁、共享模式锁,其中后两种锁都是强制锁的变种。

锁的类型 实现接口 锁的功能
劝告锁 flock(...)、 fcntl(...) 、 lockf(...) 劝告锁是一种协同工作的锁。对于这一种锁来说,内核只提供加锁以及检测文件是否已经加锁的手段,但是内核并不参与锁的控制和协调。
强制锁 fcntl(...) lockf(...) 强制锁是一种内核强制采用的文件锁。每当有系统调用 open()、read() 以及write()发生的时候,内核都要检查并确保这些系统调用不会违反在所访问文件上加的强制锁约束。
租赁锁 fcntl(...) 租赁锁实际上是强制锁的变种,当进程尝试打开一个被租借锁保护的文件时,该进程会被阻塞,同时,在一定时间内拥有该文件租借锁的进程会收到一个信号。收到信号之后,拥有该文件租借锁的进程会首先更新文件,从而保证了文件内容的一致性,接着,该进程释放这个租借锁。如果拥有租借锁的进程在一定的时间间隔内没有完成工作,内核就会自动删除这个租借锁或者将该锁进行降级,从而允许被阻塞的进程继续工作。
共享模式锁 flock(...) 共享模式强制锁可以用于某些私有网络文件系统,如果某个文件被加上了共享模式强制锁,那么其他进程打开该文件的时候不能与该文件的共享模式强制锁所设置的访问模式相冲突。但是由于可移植性不好,因此并不建议使用这种锁。

关于flock(...)、lockf(…)、fcntl(...)的说明:

  • flock(...):适用于劝告锁,只能实现对整个文件进行加锁,而不能实现记录级的加锁。但是flock实现了共享强制锁;
  • fcntl(...):适用于劝告锁和强制锁,不仅能够对整个文件进行加锁,而且能够对文件中的记录(若干字节)加锁,实际上属于posix系列的锁。
  • lockf(...):由于fcntl(...)函数功能非常多,文件锁只是其中一个。lockf(...)是fcntl(...)锁功能的包装。

三、通过Python调用Linux文件锁接口函数

#!/usr/bin/python
# coding:utf8
import os
import fcntl
import struct
def my_fcntl(fd, cmd, l_type, l_len=0, l_whence=0, l_start=0):
    """
    调用fcntl函数
    :param fd: 文件句柄,可以这样获取:
                fobj = open(name, 'w')
                fd = fobj.fileno()
    :param cmd: fcntl函数要执行的操作,于文件锁相关的有:
                fcntl.F_SETLKW  :请求文件锁,如果锁被占用,那么等待
                fcntl.F_GETLK   :检查锁的状态,或者对文件锁unlock
                fcntl.F_SETLK   :请求文件锁,如果锁被占用,抛出异常
    :param l_type: 锁的类型
                fcntl.F_WRLCK   :写文件锁
                fcntl.F_RDLCK   :读文件锁
                fcntl.F_UNLCK   :释放锁
    :param l_len: 需要锁的字节长度
    :param l_whence:度量l_start,0表示从头开始,1表示从当前位置开始,2表示从文件结尾开始
    :param l_start:需要锁定的字节的起始位置
    :return:
    """
    flock = struct.pack('hhllhh', l_type, l_whence, l_start, l_len, 0, os.getpid())
    flock_msg = fcntl.fcntl(fd, cmd, flock)
    return struct.unpack('hhllhh', flock_msg)
    
def my_flock(fd,lock_type):
    """
    调用flock对文件加锁
    :param fd: 文件句柄
    :param lock_type:
            # LOCK_SH: 共享锁
            # LOCK_EX: 排他锁
            # LOCK_NB: 非阻塞锁(可以和LOCK_SH、LOCK_EX一起使用)
            # LOCK_UN: 释放锁
            # LOCK_MAND: 创建共享强制锁,当时并非所有文件系统都实现了这个模式
            # LOCK_READ: 配合LOCK_MAND使用
            # LOCK_WRITE: 配合LOCK_MAND使用
    :return:
    """
fcntl.flock(fd,lock_type)

四、NFS的加锁机制

在NFS2、NFS3协议本身不支持文件锁,实现对NFS文件系统的中文件加锁需要通过Network Lock Manager(NLM)这个辅助工具。NFS4协议在协议中实现了文件锁,使NFS可以脱离NLM管理文件锁。
在一个NFS目录下对某个文件加锁

    touch locker
    flock -xn "locker" -c "sleep 1000"

登录NFS4服务端,查看文件的锁状态(这里的服务端时NetApp,通过vserver locks show命令可以看到哪些文件被加了锁)。

图片.png

从服务端返回的信息来看,/NFS/locker文件上有两个锁,share-level模式的锁是NFS协议本身的文件锁,而byte-range模式的锁是flock命令施加的!
参考man nfs,可以发现以下两个和NFS文件锁相关的mount参数:
对应命令
mount -o lock -o local_lock=none <ip>:/<space> <standpoint>

参数 说明
lock/ nolock 选择是否使用 NLM协议来支持NFS的文件锁(默认情况下是lock)
local_lock NFS文件是否采用本地锁,可以有的选择有all, flock, posix, none,对应flock锁和posix形式的锁(fctnl函数生成的锁),默认情况下是none。

lock / nolock和local_lock选项是相互影响的,主要在NFS3协议中起作用,根据两个参数的不同选项,有如下情形:

mount参数 flock锁 posix锁
lock, local_lock=none 所有NFS客户端可见 所有NFS客户端可见
lock,local_lock=flock 只有本地可见 所有NFS客户端可见
lock,local_lock=posix 所有NFS客户端可见 只有本地可见
lock,local_lock=all 只有本地可见 只有本地可见
nolock,local_lock=all/flock/ posix 只有本地可见 只有本地可见
nolock,local_lock=none 所有NFS客户端可见 所有NFS客户端可见

在NFS4协议下,由于协议本身自带文件锁状态,因此在NFS4协议下local_lock配置项是没有用的,此时使用flock或者posix加锁NFS所有客户端都可见。
此外在NFS4协议下,打开文件还会生成一个share-level级别的共享锁,如下图:

图片.png

其中,Sharelock Mode为read_write-deny_none,该模式中“read_write”表示某个客户端以读写模块式打开文件,“deny_none”表示打开文件的客户端,不对其他客户端的文件操作进行限制。

参考NFS4协议文档,NFS客户端可以编程控制NFS4共享锁的模式:

# 获取锁的Client的操作
const OPEN4_SHARE_ACCESS_READ = 0x00000001;
const OPEN4_SHARE_ACCESS_WRITE = 0x00000002;
const OPEN4_SHARE_ACCESS_BOTH = 0x00000003;

# 未获取锁的Client被允许的操作
const OPEN4_SHARE_DENY_NONE = 0x00000000;
const OPEN4_SHARE_DENY_READ = 0x00000001;
const OPEN4_SHARE_DENY_WRITE = 0x00000002;
const OPEN4_SHARE_DENY_BOTH = 0x00000003;

五、参考

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

推荐阅读更多精彩内容