zoukankan      html  css  js  c++  java
  • 记录锁

    记录锁

    基本概念

    若两个人同时编辑一个文件,其后果将如何呢?在很多unix系统中,该文件的最后状态取决于写该文件的最后一个进程。但是对于有些应用程序(例如数据库),进程有时需要确保它正在单独写一个文件。为了向进程提供这种功能,商用unix系统提供了记录锁机制。

    记录锁的功能是:当一个进程正在读或修改文件的某个部分时,它可以阻止其他进程修改同一文件区。对于unix系统而言,"记录"该词并不恰当,更适合的术语可能是字节范围锁(byte-range locking),因为它锁定的只是文件中的一个区域(当然,也可能是整个文件)。

    POSIX.1 形式记录锁

    本文主要介绍POSIX.1的fcntl锁,其函数原型为:

    #include <fcntl.h>

    int fcntl(int filedes, int cmd, ... /* struct flock *flockptr */ );

    返回值:若成功则依赖于cmd,若出错则返回-1

    第三个参数flockptr是一个指向flock结构的指针:

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

    对flock结构说明如下:

    1)所希望的锁类型:F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)或F_UNLCK(解锁一个区域)。

    2)要加锁或解锁区域的起始位置由l_start和l_whence两者决定。

    3)区域的长度由l_len表示。

    4)ID为l_pid的进程持有一把当前进程请求的锁,使当前进程阻塞(仅由F_GETLK返回)

    以下是对加锁和解锁区域的一些说明:

    1)l_start和l_whence两参数的用法和lseek函数的最后两参数类似,l_whence可选用的值是SEEK_SET,SEEK_CUR,SEEK_END。

    2)该区域可以在文件的当前尾端或之后开始,但是不能在文件的起始位置之前开始。

    3)若l_len为0,则表示锁的区域从其起点(由l_start和l_whence决定)开始直至最大可能偏移量为止,也就是说不管向该文件中添加多少数据,它们都处于锁的范围内。

    4)为了锁整个文件,我们设置l_start和l_whence,使锁的起点在文件起始处,并且使l_len为0(有多种方法可以指定文件起始处,最常用的做法是使l_start为0,l_whence为SEEK_SET)。

    共享读锁和独占写锁的基本规则:

    多个进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定字节上只能有一个进程独占的写锁。进一步而言,如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个字节上已经有一把独占性的写锁,则不能再对它加任何读锁。

    以上规则适合于不同进程提出的锁请求,并不适用于单个进程提出的多个锁请求。如果一个进程对一个文件区域已经有了一把锁,后来该进程又企图在同一文件区域再加一把锁,那么新锁将替换掉老锁。

    加(获取)读锁时,该描述符必须是读打开;加写锁时,该描述符必须是写打开。

    fcntl函数的三种锁处理命令

    F_GETLK   判断由flockptr所描述的锁是否会被另外一把锁所阻塞。如果现存一把锁,它阻止创建由flockptr所描述的锁,则用该现存锁的信息覆盖flockptr指向的结构。如果不存在上述情况,则除了将l_type设置为F_UNLCK之外,flockptr所指向结构中的其他信息保持不变。

    F_SETLK  设置由flockptr所描述的锁。如果试图建立一把读锁(l_type设为F_RDLCK)或写锁(l_type设为F_WRLCK),而按上述兼容性规则不被允许,则fcntl立即出错返回,此时errno设置为EACCES或EAGAIN。

    此命令也用来清除由flockptr说明的锁(l_type为F_UNLCK)

    F_SETLKW  这是F_SETLK的阻塞版本(命令名中的W表示wait)。如果因为当前在所请求区间的某个部分已被另一个进程锁住,按兼容性规则,由flockptr所请求的锁不能被创建,调用进程进入休眠。如果请求创建的锁已经可用,或者休眠状态被某个信号中断,则该进程被唤醒。

    注意:在设置或释放文件上的锁时,系统按要求组合或裂开相邻区。

    实例:请求和释放一把锁

    为了避免每次分配flock结构,然后又填入各项信息,可以用下面的lock_reg函数来处理所有细节:

    #include <fcntl.h>
    int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
    {
         struct flock lock;
         
         lock.l_type = type;  /*  F_RDLCK, F_WRLCK, F_UNLCK  */
         lock.l_start = offset; /* byte offset, relative to l_whence */
         lock.whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END*/
         lock.l_len = len;   /* #bytes (0 means to EOF) */
    
         return (fcntl(fd, cmd, &lock));
    } 

    因为大多数锁调用是加锁或解锁一个文件区域(命令F_GETLK极少使用),我们通常使用下列5个宏:

    #define read_lock(fd, offset, whence, len) 
                      lock_reg( (fd), F_SETLK, F_RDLCK, (offset), (whence), (len) )
    #define readw_lock(fd, offset, whence, len) 
                      lock_reg( (fd), F_SETLKW, F_RDLCK, (offset), (whence), (len) )
    #define write_lock(fd, offset, whence, len) 
                      lock_reg( (fd), F_SETLK, F_WRLCK, (offset), (whence), (len) )
    #define writew_lock(fd, offset, whence, len) 
                      lock_reg( (fd), F_SETLKW, F_WRLCK, (offset), (whence), (len) )
    #define un_lock(fd, offset, whence, len) 
                      lock_reg( (fd), F_SETLK, F_UNLCK, (offset), (whence), (len) )

    记录锁的自动继承和释放规则

    1)锁是与进程、文件两者相关联的。这有两重含义:第一重很明显,当一个进程终止时,它锁建立的锁全部释放;第二重意思不是很明显,任何时候关闭一个描述符时,则该进程通过这一描述符可以引用的文件上的任何一把锁都被释放(这些锁都是改进程设置的)。

    2)由fork产生的子进程不继承父进程所设置的锁。这意味着,若一个进程得到一把锁,然后调用fork,那么对于父进程获得的锁而言,子进程被视为另一个进程,对于从父进程处继承过来的任一描述符,子进程需要调用fcntl才能获得它自己的锁。这与锁的作用是相一致的。锁的作用是阻止多个进程同时写同一个文件(或同一个文件区域)。如果子进程继承父进程的锁,那么父、子进程就可以同时写同一个文件。

    3)在执行exec后,新程序可以继承原执行程序的锁。但是,如果对一个文件描述符设置了close-on-exec标志,那么当作为exec的一部分关闭该文件描述符时,对应文件的所有锁都被释放了。

    强制性锁

    强制性锁使内核对每一个open、read和write系统调用都进行检查,检查调用进程对正在访问的文件是否违背了某一把锁的作用。强制性锁有时也被称为强迫方式锁。

    对一个特定文件打开其设置组ID位并关闭其组执行位,则对该文件开启了强制性锁机制。

  • 相关阅读:
    POJ3662 Telephone Lines (dijkstra+二分)
    Codeforces Round #618 (Div. 2)A. Non-zero
    Codeforces Round #618 (Div. 2)C. Anu Has a Function
    洛谷P1060开心的金明(滚动数组优化)
    洛谷P1006传纸条
    Spring Boot中以代码方式配置Tomcat
    js常用方法总结(以后遇到再进一步总结)
    localStorage的使用
    巧用Ajax的beforeSend 提高用户体验
    四种会话跟踪技术
  • 原文地址:https://www.cnblogs.com/bettercoder/p/3505834.html
Copyright © 2011-2022 走看看