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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 3D游戏模型是怎么制作出来的?[通俗易懂]

    现在游戏建模这个行业非常热门,因此有很多小伙伴梦想成为一名游戏模型师,但是很多都对这个工作需要做什么了解不深,这里我简单介绍一下我从事的3D游戏模型制作。1.我们都知道游戏模型制作肯定是需要建模的,建模是游戏设计中最重要的环节,占整个模型制作的70%,建模的方法很多,游戏模型常用的软件有3DMAX(游戏制作中使用的软件很多都是英文版,建议学习从英文软件开始)建模的难度在所有职业中是最高最全面的,从桌椅到星舰,巨型BOSS等等复杂的模型,模型这方面没有什么多说的,练习是最重要的。具体的方法技巧可.

  • python数据可视化_python中datetime用法

    python数据可视化_python中datetime用法xiao.77如何通过Python写入date数据了?写入还是很简单的。importxlwt3importdatetimeasdtworkbook=xlwt.Workbook()worksheet=workbook.add_sheet(‘sheet1’)worksheet.write(0,0,dt.date.today())workbook.save(‘test.xls’)查…

  • js十大算法[通俗易懂]

    js十大算法[通俗易懂]JS的十大经典算法冒泡排序(BubbleSort)冒泡排序须知:作为最简单的排序算法之一,冒泡排序给我的感觉就像Abandon在单词书里出现的感觉一样,每次都在第一页第一位,所以最熟悉。。。冒泡排序还有一种优化算法,就是立一个flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。但这种改进对于提升性能来说并没有什么太大作用。。。什么时候最快(BestCases):当输入的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何用啊。。。。)什么时候最慢…

  • 点击展开显示折叠内容

    点击展开显示折叠内容

  • webpack版本问题_webpack查询有没有安装

    webpack版本问题_webpack查询有没有安装卸载后重新安装卸载重新安装卸载#卸载全局npmuninstall-gwebpack-clinpmuninstall-gwebpack#卸载局部npmuninstallwebpack-clinpmuninstallwebpack重新安装npminstall-g–save-devwebpack@version#不指定版本,默认安装最新npminstall-g–save-devwebpack-cli#查看webpack-v#若命令不存在,

  • 双边滤波算法_双边滤波的原理

    双边滤波算法_双边滤波的原理双边滤波算法

发表回复

您的电子邮箱地址不会被公开。

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