Linux文件—文件锁

Linux文件—文件锁通过之前的open()/close()/read()/write()/lseek()函数已经可以实现文件的打开、关闭、读写等基本操作,但是这些基本操作是不够的。对于文件的操作而言,“锁定”操作是对文件(尤其是对共享文件)的一种高级的文件操作。当某进程在更新文件内数据时,期望某种机制能防止多个进程同时更新文件从而导致数据丢失,或者防止文件内容在未更新完毕时被读取并引发后续问题,这种机制就是“文件锁”。

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

通过之前的open()/close()/read()/write()/lseek()函数已经可以实现文件的打开、关闭、读写等基本操作,但是这些基本操作是不够的。

对于文件的操作而言,“锁定”操作是对文件(尤其是对共享文件)的一种高级的文件操作。当某进程在更新文件内数据时,期望某种机制能防止多个进程同时更新文件从而导致数据丢失,或者防止文件内容在未更新完毕时被读取并引发后续问题,这种机制就是“文件锁”。

对于共享文件而言,不同的进程对同一个文件进行同时读写操作将极有可能出现读写错误、数据乱码等情况。在Linux系统中,通常采用“文件锁”的方式,当某个进程独占资源的时候,该资源被锁定,其他进程无法访问,这样就解决了共享资源的竞争问题。

文件锁包括建议性锁(又名“协同锁”)和强制性锁两种。建议性锁要求每个相关进程访问文件的时候检查是否已经有锁存在并尊重当前的锁。一般情况下不建议使用建议性锁,因为无法保证每个进程都能自动检测是否有锁,Linux内核与系统总体上都坚持不使用建议性锁。而强制性锁是由内核指定的锁,当一个文件被加强制性锁的过程中,直至该所被释放之前,内核将阻止其他任何进程对该文件进行读或写操作,每次读或写操作都得检测锁是否存在。当然,采用强制性锁对内核的性能影响较大,每次内核在操作文件的时候都需要检查是否有强制性锁。

在Linux内核提供的系统调用中,实现文件上锁的函数有lockf()fcntl(),其中lockf()用于对文件加建议性锁,这里不再讲解。fcntl()函数既可以加建议性锁,也可以加强制性锁。同时,fcntl()还能对文件某部分上记录锁。所谓记录锁,其实就是字节范围锁,它能锁定文件内某个特定区域,当然也可锁定整个文件。

记录锁又分为读锁和写锁两种。其中读锁又称为共享锁,它用来防止进程读取的文件记录被更改。记录内可设置多个读锁,但当有一个读锁存在的时候就不能在该记录区域设置写锁。写锁又称为排斥锁,在任何时刻只能有一个程序对文件的记录加写锁,它用来保证文件记录被某一进程更新数据的时候不被其他进程干扰,确保文件数据的正确性,同时也避免其他进程“弄脏”数据。文件记录一旦被设置写锁,就不能再设置任何锁直至该写锁解锁。

函数fcntl()
需要头文件:#include< unistd.h>
#include< sys/types.h>
#include< fcntl.h>
函数原型:int fcntl(int fd,int cmd,struct flock *lock_set);
函数参数:fd:文件描述符
cmd:检测锁或设置锁
lock_set:结构体类型指针,结构体struct flock需要事先设置,与第二个参数连用
函数返回值:成功:0
失败:-1
第二个参数cmd表示该操作对文件的命令,若该命令是对文件检测锁或施加锁,则需要第三个参数:
F_GETLK:检测文件锁状态,检测结果存放在第三个参数的结构体的l_type内
F_SETLK:对文件进行锁操作,锁操作类型存放在第三个参数的结构体的l_type内
F_SETLKW:同F_SETLK,不过使用该参数时若不能对文件进行锁操作则会阻塞直至可以进行锁操作为止(W即wait,等待)
(更多参数请参阅fcntl()函数的使用手册)
第三个参数是对文件施加锁操作的相关参数设置的结构体
注意:必须定义struct flock类型结构体并初始化结构体内的数据,然后使用地址传递的方式传递参数,不允许直接定义struct flock* 类型指针直接传参

关于struct flock的成员如下:

struct flock
{
    short l_type;
    short l_whence;
    off_t l_start;
    off_t l_len;
    pid_t l_pid;
}

结构体成员说明:
l_type:有三个参数
F_RDLCK:读锁(共享锁)
F_WRLCK:写锁(排斥锁)
F_UNLCK:无锁/解锁
l_whence:相对于偏移量的起点,参数等同于fseek()与lseek()中的whence参数
SEEK_SET:位置为文件开头位置
SEEK_CUR:位置为文件当前读写位置
SEEK_END:位置为文件结尾位置
l_start:加锁区域在文件中的相对位移量,与l_whence的值共同决定加锁区域的起始位置
l_len:加锁区域的长度,若为0则表示直至文件结尾EOF
l_pid:具有阻塞当前进程的锁,其持有的进程号会存放在l_pid中,仅由F_GETLK返回

示例:使用fcntl()函数对文件进行锁操作。
首先初始化结构体flock中的值,然后调用两次fcntl()函数。第一次参数设定为F_GETLK判断是否可以执行flock内所描述的锁操作;第二次参数设定为F_SETLK或F_SETLKW对该文件进行锁操作。
注意:需要至少两个终端运行该程序才能看到效果

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
int lock_set(int fd,int type)
{
    struct flock lock;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;//三个参数设置锁的范围是全文件
    lock.l_type = type;//type的参数由主调函数传参而来
    lock.l_pid = -1;

    //第一次操作,判断该文件是否可以上锁
    fcntl(fd,F_GETLK,&lock);
    if(lock.l_type!=F_UNLCK)//如果l_type得到的返回值不是F_UNLCK则证明不能加锁,需判断原因
    {
        if(lock.l_type==F_RDLCK)
        {
            printf("This is a ReadLock set by %d\n",lock.l_pid);
        }
        else if(lock.l_type==F_WRLCK)
        {
            printf("This is a WriteLock set by %d\n",lock.l_pid);
        }
    }

    //第二次操作,对文件进行相应锁操作
    lock.l_type = type;
    if((fcntl(fd,F_SETLKW,&lock))<0)
    {
        printf("Lock Failed:type = %d\n",lock.l_type);
        return -1;
    }
    switch(lock.l_type)
    {
        case F_RDLCK:
            printf("ReadLock set by %d\n",getpid());break;
        case F_WRLCK:
            printf("WriteLock set by %d\n",getpid());break;
        case F_UNLCK:
            printf("ReleaseLock by %d\n",getpid());
            return 1;
            break;
    }
    return 0;
}
int main(int argc, const char *argv[])
{
    int fd;
    if((fd=open("hello.txt",O_RDWR))<0)
    {

        perror("fail to open hello.txt");
        exit(0);
    }
    printf("This pid_no is %d\n",getpid());
    //给文件上锁
    lock_set(fd,F_WRLCK);
    printf("Press ENTER to continue...\n");
    getchar();
    //给文件解锁
    lock_set(fd,F_UNLCK);
    close(fd);
    return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

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

(0)
上一篇 2022年6月28日 下午5:16
下一篇 2022年6月28日 下午5:16


相关推荐

  • js中的prototype的解析

    js中的prototype的解析js的方法可以分为三种:对象方法、类方法、prototype方法//对象方法functionPeople(name){this.name=name;this.introduct=function(){alert(“Mynameis”+this.name);console.log(“Mynameis”+this.name);}}//类方法Peop

    2022年7月23日
    11
  • 如何实现微信上制作活动链接「建议收藏」

    如何实现微信上制作活动链接「建议收藏」随着互联网的快速发展,无论是房产、装修检查、家居、家店还是商城、餐饮等行业,商家们都会用到活动预约报名,线上活动链接的制作不仅成本低,而且受众也广,可以达到快速宣传的效果。相信很多小伙伴们在微信朋友圈看到的微信活动报名链接很好奇,这种活动链接是如何实现的,希望自己也可以在微信上制作这种活动链接。    工预善其事必先利其器,在这里,咱不能不提到一个非常好用的微信活动制作神器—获客宝。这款软件的神奇之处在于,他不仅可以帮你在微信上制作活动页面,而且还可以帮你侦查到谁偷偷浏览了你的页面(悄悄来,又悄悄走,不

    2026年1月18日
    6
  • 深度强化学习-DQN算法原理与代码

    深度强化学习-DQN算法原理与代码DQN 算法是 DeepMind 团队提出的一种深度强化学习算法 在许多电动游戏中达到人类玩家甚至超越人类玩家的水准 本文就带领大家了解一下这个算法 论文的链接见下方 论文 https www nature com articles nature14236 pdf 代码 后续会将代码上传到 Github 上 1DQN 算法简介 Q learning 算法采用一个 Q tabel 来记录每个状态下的动作值 当状态空间或动作空间较大时 需要的存储空间也会较大 如果状态空间或动作空间连续 则该算法无法使用 因

    2026年3月19日
    1
  • Idea激活码最新教程2021.2.4版本,永久有效激活码,亲测可用,记得收藏

    Idea激活码最新教程2021.2.4版本,永久有效激活码,亲测可用,记得收藏Idea 激活码教程永久有效 2021 2 4 激活码教程 Windows 版永久激活 持续更新 Idea 激活码 2021 2 4 成功激活

    2025年5月22日
    5
  • python进制转换函数-Python中进制转换函数的使用

    python进制转换函数-Python中进制转换函数的使用Python中进制转换函数的使用关于Python中几个进制转换的函数使用方法,做一个简单的使用方法的介绍,我们常用的进制转换函数常用的就是int()(其他进制转换到十进制)、bin()(十进制转换到二进制)、oct()(十进制转换到八进制)、hex()(十进制转换到十六进制)。下面我们逐个说下每个函数的用法。binbin()函数,是将十进制的数字转换成二进制的数字。其中bin()函数中传入的是十进…

    2022年5月19日
    38
  • Cursor无限免费全方位指南!

    Cursor无限免费全方位指南!

    2026年3月16日
    2

发表回复

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

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