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)


相关推荐

  • 小程序即时通讯聊天控件(一)

    小程序即时通讯聊天控件(一)小程序即时通讯——文本、语音输入控件(一)集成近期一直在做微信小程序,业务上要求在小程序里实现即时通讯的功能。这部分功能需要用到文本和语音输入及一些语音相关的手势操作。所以我写了一个控件来处理这些操作。控件样式我们先来看下效果目前的功能就是动态图中展示的,我们可以使用这个控件来切换输入方式(文本或语音)、获取到输入的信息、取消语音输入、语音消息录制过短过长的判断(该接口暂时还未开放),支持发送图片和

  • copy.deepcopy()_python列表copy函数

    copy.deepcopy()_python列表copy函数python中对于对象的拷贝分为浅拷贝(copy)和深拷贝(deepcopy)两种方式。其中浅拷贝由“=”完成。而深拷贝由copy模块中deepcopy()函数担任。浅拷贝和深拷贝的区别是:浅拷贝只是将原对象在内存中引用地址拷贝过来了。让新的对象指向这个地址。而深拷贝是将这个对象的所有内容遍历拷贝过来了,相当于跟原来没关系了,所以如果你这时候修改原来对象的值跟他没关系了,不会随…

  • linux复制多个文件到另一个文件命令_怎么替换安装目录下的文件

    linux复制多个文件到另一个文件命令_怎么替换安装目录下的文件使用参数-R

  • 可视化希尔排序算法是什么_希尔排序一趟排序的结果

    可视化希尔排序算法是什么_希尔排序一趟排序的结果如需转载请标明出处:https://blog.csdn.net/zhuzi9QQ技术交流群:594200841前言概念介绍希尔排序是基于插入排序算法的一种更高效的改进版本。它是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越少,当增量减少至1时,整个文件恰被分成一组。此时算法便终止。原理讲解以[41243421917]这个序列为例说明希尔排序算法的实现原理未开始遍历时,此时效果如下图由上面数组可知

  • 黑盒测试用例设计之nextdate问题[通俗易懂]

    黑盒测试用例设计之nextdate问题[通俗易懂]首先已知有三个变量:月份,日期和年变量月份,日期和年都为整数,且都满足条件:1<=月份<=121<=日期<=311912<=年<=2012等价类划分法1.首先输入数据,划分等价类2.建立等价类表3.设计测试用例原型4.考虑隐含需求分为平年和闰年进行讨论,主要针对二月份。边界值分析法首先明晰三个定义:内点:范围内部的点上点:边界…

  • nginx简单配置多个server

    nginx简单配置多个server1:安装nginx步骤就不说了,自行百度。2:打开nginx的配置文件nginx.conf这是项目1的配置,现在需要再开个同域名不同端口的项目,如下图:注意:LZ一直出现访问不了,折腾了许久,是因为服务器www.pigaudio.com或120.77.223.7只开了默认的80端口,而8088端口并未开,所以只需要登陆你的服务账号添加一个8088即可,比如你的服务器是阿里云购买的,则需要登陆阿里…

    2022年10月31日

发表回复

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

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