Linux文件锁

Linux文件锁翻阅参考资料,你会发现文件锁可以进行很多的分类,最常见的主要有读锁与写锁,前者也叫共享锁,后者也叫排斥锁,值得注意的是,多个读锁之间是不会相互干扰的,多个进程可以在同一时刻对同一个文件加读锁;但是,如果已经有一个进程对该文件加了写锁,那么其他进程则不能对该文件加读锁或者写锁,直到这个进程将写锁释放,因此可以总结为:对于同一个文件而言,它可以同时拥有多个读者,但是在某一时刻,他只能拥有一个写者。

大家好,又见面了,我是你们的朋友全栈君。

一、文件锁的分类:

        翻阅参考资料,你会发现文件锁可以进行很多的分类,最常见的主要有读锁与写锁,前者也叫共享锁,后者也叫排斥锁,值得注意的是,多个读锁之间是不会相互干扰的,多个进程可以在同一时刻对同一个文件加读锁;但是,如果已经有一个进程对该文件加了写锁,那么其他进程则不能对该文件加读锁或者写锁,直到这个进程将写锁释放,因此可以总结为:对于同一个文件而言,它可以同时拥有多个读者,但是在某一时刻,他只能拥有一个写者。

      根据内核行为来分,文件锁可以分成劝告锁强制锁两大类:

      1.  劝告锁:

         劝告锁讲究的是一种协同工作,内核仅负责对文件加锁以及检查文件是否已经上锁等操作,而不亲自去参与文件锁的控制与协调,而这些都需要程序员首先要检查所要访问的文件之前是否已经被其他进程加锁来实现并发控制,劝告锁不仅可以对文件的任一部分加锁,也可以对整个文件加锁。下面是加锁规则:

Linux文件锁

        2.强制锁:

           强制锁则是内核强制使用的一种文件锁,每当有进程违反锁规则,内核将会进行阻止,具体的加锁规则如下:
           (1)若一个文件已经加上共享锁,那么其他进程在对这个文件进行写操作时将会被内核阻止;
           (2)若一个文件已经加上了排他锁,那么其他进程对这个文件的读取与写操作都将被阻止;
            下表总结了进程试图访问已经加有强制锁的文件,进程行为如下:

Linux文件锁

           从上表可以看出,若进程要访问文件的锁类型与要进行的操作存在冲突,那么若操作时在阻塞时进行,则进程将会阻塞;若操作时在非阻塞时进程,则进程将会立即返回EAGIN错误(PS:表示资源临时不可达)。
           根据加锁区域范围,可以分成整个文件锁与区域文件锁(记录锁),二者很好区分,前者可以锁定整个文件,而后者则可以锁定文件中的某一区域,甚至是某几个字节。

二、文件锁相关的系统调用:

            目前跟文件加锁相关的系统调用主要有两个:
flock与fcntl, 二者在应用范围方面也存在着一些差别,早起的flock函数只能处理劝告锁,在Linux 2.6版本中将其功能扩充至强制锁,另外
flock函数只能对整个文件加锁,不能加记录锁,而fcntl函数则不仅完全支持加劝告锁与强制锁,还支持记录锁,另外因为它符合POSIX标准,具有很好的可移植性

         值得注意的是,在给文件加锁之前,一定要保证文件以相应的访问模式打开,例如要对一个文件加上共享锁,一定要首先按读模式打开文件,若要给文件加上排他锁,则首先要按写模式打开对应文件若想加两种锁,则需要按读写模式打开.

        int fcntl(int fd, int cmd, struct flock*lock)  

        fcntl函数专门用来对文件描述符操作的,具体的操作行为取决于cmd值,与本文文件锁相关的cmd值主要有:

        F_GETLK:获取文件锁

        F_SETLK:设置文件锁(非阻塞版)

        F_SETLKW:设置文件锁(阻塞版)

         值得注意的是,调用F_SETLKW命令去设置文件锁的请求不能完成,则进程将会进入休眠状态,直至所要求的锁被释放。其它更多的cmd值可以参考《UNIX环境高级编程》或者”man fcntl”。

         lock参数主要是用来实现指定文件锁类型、所锁定的文件范围以及正在锁定文件的进程ID(只是在获取文件锁时才会用到),详细结构如下:

           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 (F_GETLK only) */
           };

        其中l_type定义所的类型,F_RDLCK表示共享锁,F_WRLCK表示排他锁,F_UNLCK表示释放掉之前已经建立的锁;l_whence, l_start与l_len共同作用设置所加锁的范围,其中l_whence设置锁的参照起始点,SEEK_SET表示文件开头,SEEK_CUR表示文件当前位置(fseek可以移动文件指针位置),SEEK_END表示文件结尾;l_start与l_whence相结合确定了锁的绝对起始点,l_len则表示从绝对起始点开始需要锁定的字节数,其值可正可负,锁的范围则是[l_start, l_start+l_len-1],若其值为0,则有特殊的含义,表示锁的区域从绝对起始点开始到最大可能的偏移量为止,这种情况可用于锁定整个文件,此时只需将锁的绝对起始点设置为文件开始位置即可。

         函数的返回值是:若成功则返回0,否则返回-1.

         int flock(int fd, int operation)

         相对于fcntl函数,flock显得更加简单,因为所加的锁会影响整个文件,其中operation参数规定了所加锁的类型:

         LOCK_SH:表示加共享锁

         LOCK_EX:表示排他锁

         LOCK_UN:表示释放锁

         LOCK_MAND:表示强制锁

三、锁的继承与释放:

       1.      锁与进程和文件紧密相连,若进程终止,则有它创建的所有锁将会自动释放掉;若关闭文件描述符,则进程由此描述符引用的文件上的任何锁也将会被释放

       2.      由fork产生的子进程不会继承父进程的文件锁;

       3.      在执行exec之后,新程序可以继承原来程序的文件锁。

跟锁有关的封装函数(来自于《高级UNIX环境编程》如下测试例子:

/***************************************file_lock.h*******************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>      
#include <sys/stat.h>


int   lock_reg(int, int, int, off_t, int, off_t); //register lock
pid_t lock_test(int, int, off_t, int, off_t);  //test  lockable


//set lock
#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))
            
//Test lock    
#define read_lock_pid(fd, offset, whence, len) \
      lock_test((fd), F_RDLCK, (offset), (whence), (len))
#define write_lock_pid(fd, offset, whence, len) \
      lock_test((fd), F_WRLCK, (offset), (whence), (len))
#define is_read_lockable(fd, offset, whence, len) \
            (read_lock_pid == 0)
#define is_write_lockable(fd, offset, whence, len) \
            (write_lock_pid == 0)     
/******************************************file_lock.c****************************************/
#include "file_lock.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.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
    lock.l_len = len;       /* #bytes (0 means to EOF) */

    return(fcntl(fd, cmd, &lock));
}


pid_t lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
    struct flock    lock;

    lock.l_type = type;     /* F_RDLCK or F_WRLCK */
    lock.l_start = offset;  /* byte offset, relative to l_whence */
    lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
    lock.l_len = len;       /* #bytes (0 means to EOF) */

    if (fcntl(fd, F_GETLK, &lock) < 0)
  {
     printf("fcntl error for %s.\n", strerror(errno));
     return (-1);
  }     

    if (lock.l_type == F_UNLCK)
  {
    return(0);    /* false, region isn't locked by another proc */
  } 
        
    return(lock.l_pid); /* true, return pid of lock owner */
}
/*****************************************file_lock.c*****************************************/
#include "file_lock.h"

static void lock_set(int fd, int type) ;

int main(int argc, char **argv)
{
    int fd ;
    
    //First open file and choose right mode by lock type 
    fd = open("/tmp/hello", O_RDWR | O_CREAT, 0666) ;
    if (fd < 0) 
    {
        printf("open error ! \n") ;
        exit(1) ;
    }
    
    //lock
    printf("press ENTER to add lock:\n");
    getchar() ;
    lock_set(fd, F_WRLCK) ;
    
    
    printf("press ENTER and exit, lock release:\n");
    getchar() ; 
    //release lock
    lock_set(fd, F_UNLCK) ; //useless code, lock release if close fd
    if (close(fd) < 0)
    {
        printf("\n close file error ! \n") ;
        exit(1) ;
    }   
    
    return 0 ;
}

void lock_set(int fd, int type) 
{
    pid_t read_lock_id = 0;
    pid_t write_lock_id = 0;
    
    while (1)
    {
        //set lock according to lock type
        switch (type)
        {
            case F_RDLCK:
                if (read_lock(fd, 0, SEEK_SET, 0) == 0)
                {
                  printf("read lock set by %d \n", getpid());
                  return;
                }
                break;
            case F_WRLCK:
                if (write_lock(fd, 0, SEEK_SET, 0) == 0)
                {
                  printf("write lock set by %d \n", getpid());
                  return;
                }
                break;
            case F_UNLCK:
                if (un_lock(fd, 0, SEEK_SET, 0) == 0)
                {
                  printf("release lock by %d \n", getpid());
                  return;
                }
                break;
      }  
    
      //test lock owner
      if (type == F_RDLCK)
      {
          if ((read_lock_id = read_lock_pid(fd, 0, SEEK_SET, 0)) != 0)
          {
            printf("read lock already set by %d \n", read_lock_id);
          }
      }
      else if (type == F_WRLCK)
      {
          if ((write_lock_id = read_lock_pid(fd, 0, SEEK_SET, 0)) != 0)
          {
            printf("write lock already set by %d \n", write_lock_id);
          }    
      }
    }
}
/****************************************Makefile*********************************************/
all: file_lock_test

CC = gcc

file_lock_test:
        $(CC) file_syn.c file_lock.c -o file_lock_test
clean:
        rm -rf *.o file_lock_test

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/153473.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • pytest运行_python压测

    pytest运行_python压测前言pytest运行完用例之后会生成一个.pytest_cache的缓存文件夹,用于记录用例的ids和上一次失败的用例。方便我们在运行用例的时候加上–lf和–ff参数,快速运行上一

    2022年7月29日
    6
  • c语言函数回调详解_c语言回调函数例子

    c语言函数回调详解_c语言回调函数例子关于静态库和动态库的使用和制作方法。http://blog.csdn.net/morixinguan/article/details/52451612今天我们要搞明白的一个概念叫回调函数。什么是回调函数?百度的权威解释如下:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实…

    2025年6月27日
    1
  • Gridview导出为Excel

    Gridview导出为Excel1.由于gridview的内容可能是分页显示的,因此,这里在每次导出excel时,先将gridview的allowpaging属性设置为false,然后databind()一下,确保搂到所有数据;2.

    2022年7月3日
    20
  • clion ubnutu激活码-激活码分享2022.02.18

    (clion ubnutu激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~LGWS…

    2022年4月1日
    41
  • 【译】十二因子应用(四)

    【译】十二因子应用(四)

    2021年6月17日
    96
  • SQLite下载、安装和使用并Qt链接SQLIte全部教程(windows)

    SQLite下载、安装和使用并Qt链接SQLIte全部教程(windows)第一步 下载 SQLIte 下载地址 https www sqlite org download html 下载两个内容 sqlite dll win64 x64 3360000 zipsqlite tools win32 x86 3360000 zip 下载完后直接解压 放到到一个文件夹下 这个文件夹可以随便在哪里 如下图 第二步 使用 SQLite 网上好多教程都是到这一步就配置环境变量 不知道他们脑子咋想的 轻量级数据库 SQLIte 本来就应该随着项目到处走 直接在解压且合并后

    2025年7月21日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号