Linux 文件锁 - fcntl
什么是文件锁?
即锁住文件,不让其他程序对文件做修改!
为什么要锁住文件?
案例,有两个程序,都对一个文件做写入操作。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>#define FILE_NAME "flock_demo.txt"int main(void) {int fd = -1;char buf[64] = "hello, data one!\n"; // 程序1写入文件内容//char buf[64] = "hi, data two!\n"; // 程序2写入文件内容fd = open(FILE_NAME, O_RDWR|O_CREAT, 00666);if (fd < 0) {fprintf(stderr, "open file %s failed! reason: %s\n", FILE_NAME, strerror(errno));exit(-1);}int ret = write(fd, buf, sizeof(buf));if (-1 == ret) {fprintf(stderr, "write failed. reason: %s\n", strerror(errno));exit(-2);}printf("write successful! start sleep 10s...\n");sleep(10);close(fd);return 0;
}
程序1对文件flock_demo.txt写入hello, data one!
程序2对文件flock_demo.txt写入hi, data two!
然后都睡眠10秒后才调用close函数关闭文件,然后程序结束;
首先运行程序1,然后运行程序2,来看看运行后的文件结果!

根据图片测试结果可知,程序1先将hello, data one!写入文件,然后睡眠10秒钟;然后程序2将hi, data two!写入文件,将程序1写入的内容给覆盖掉了,然后睡眠10秒;10秒后程序1执行close函数关闭文件退出程序,10秒后程序2执行close函数关闭文件退出程序。
但此时文件内容保存的是程序2写入的内容,程序1的调用者肯定就会很纳闷了,为什么我调用程序应该要写入hello, data one!才对,它为什么写入了hi, data two!呢?
如果此时程序1执行一次,而程序二不执行,那么程序1就可以正常将hello, data one!写入到文件中去,那么程序1的调用就很懵逼了,百思不得其解。。。
基于以上情况,我们操作文件前需要使用文件锁!
#include <unistd.h>
#include <fcntl.h>
int fcntl (int fd, int cmd, ... /* arg */ );
描述:文件上锁、解锁等。
fd:
文件描述符;
cmd:
取值 F_GETLK, F_SETLK 和 F_SETLKW,分别表示获取锁、设置锁和同步设置锁.
返回值:
成功: 返回 0;或者返回:F_DUPFD、F_GETFD、F_GETFL、F_GETLEASE、F_GETOWN、F_GETSIG、F_GETPIPE_SZ;对于一个成功的调用,返回值取决于操作;
失败: 返回 -1,并且设置错误编号 errno ,可用( strerror(errno) ) 查看.
文件锁的表示
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 也有三种方式:
SEEK_SET 以文件开头为锁定的起始位置。
SEEK_CUR 以目前文件读写位置为锁定的起始位置
SEEK_END 以文件结尾为锁定的起始位置。
| // struct 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 */ }; l_type: 第一个成员是加锁的类型:只读锁,读写锁,或是解锁。 l_start和l_whence: 用来指明加锁部分的开始位置。 l_len: 是加锁的长度。设0表示对整个文件加锁。 l_pid: 是加锁进程的进程id。 举例: 我们现在需要把一个文件的前三个字节加读锁,则该结构体的l_type=F_RDLCK, l_start=0, l_whence=SEEK_SET, l_len=3, l_pid=-1,然后调用fcntl函数时, cmd参数设F_SETLK. |
1. 文件上锁
struct flock fflock;
memset(&fflock, 0, sizeof(struct flock));// 上锁
fflock.l_type = F_WRLCK; /* 写锁 */
//fflock.l_type = F_RDLCK; /* 读锁 */fflock.l_whence = SEEK_SET;
fflock.l_start = 0;
fflock.l_len = 0; // 为0,锁整个文件
fflock.l_pid = -1;// 上锁或解锁,判断是否成功
//if (fcntl(fd, F_SETLKW, &fflock) < 0) {
if (fcntl(fd, F_SETLK, &fflock) < 0) {fprintf(stderr, "set file lock failed! reason: %s\n", strerror(errno));return -1;
}
2. 文件解锁
struct flock fflock;
memset(&fflock, 0, sizeof(struct flock));fflock.l_type = F_UNLCK; // 解锁
fflock.l_whence = SEEK_SET;
fflock.l_start = 0;
fflock.l_len = 0; // 为0,解锁整个文件
fflock.l_pid = -1;// 上锁或解锁,判断是否成功
//if (fcntl(fd, F_SETLKW, &fflock) < 0) {
if (fcntl(fd, F_SETLK, &fflock) < 0) {fprintf(stderr, "set file lock failed! reason: %s\n", strerror(errno));return -1;
}
3. 获取文件是否上锁
struct flock fflock;
memset(&fflock, 0, sizeof(struct flock));// 获取当前文件是否已经上锁
int ret = fcntl(fd, F_GETLK, &fflock); // F_GETLK
if (-1 == ret) {fprintf(stderr, "fcntl get lock failed! reason: %s\n", strerror(errno));return -1;
}// 文件已上锁
if (fflock.l_type != F_UNLCK) {if (fflock.l_type == F_RDLCK) { // 文件已上读锁printf("flock has been set to read lock by %d\n", fflock.l_pid);// return; } else if (fflock.l_type == F_WRLCK) { // 文件已上写锁printf("flock has been set to write lock by %d\n", fflock.l_pid);// return;}return -1;
}
例:
#include <unistd.h>
#include <fcntl.h> // file lock
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>#define FILE_NAME "flock_demo.txt"int flock_set(int fd, int type) {// 获取当前进程idprintf("pid = %d come in.\n", getpid());struct flock fflock;memset(&fflock, 0, sizeof(struct flock));// 获取当前文件是否已经上锁int ret = fcntl(fd, F_GETLK, &fflock);if (-1 == ret) {fprintf(stderr, "fcntl get lock failed! reason: %s\n", strerror(errno));return -1;}// 文件已上锁if (fflock.l_type != F_UNLCK) {if (fflock.l_type == F_RDLCK) {printf("flock has been set to read lock by %d\n", fflock.l_pid);} else if (fflock.l_type == F_WRLCK) {printf("flock has been set to write lock by %d\n", fflock.l_pid);}return -1;}// lock filefflock.l_type = type;fflock.l_whence = SEEK_SET;fflock.l_start = 0;fflock.l_len = 0;fflock.l_pid = -1;// 上锁或解锁,判断是否成功//if (fcntl(fd, F_SETLKW, &fflock) < 0) {if (fcntl(fd, F_SETLK, &fflock) < 0) {fprintf(stderr, "set file lock failed! reason: %s\n", strerror(errno));return -1;}switch(fflock.l_type) {case F_RDLCK: {printf("read lock is set by %d\n", getpid());}break;case F_WRLCK: {printf("write lock is set by %d\n", getpid());}break;case F_UNLCK: {printf("lock is released by %d\n", getpid());}break;default:break;}printf("Process pid = %d out.\n", getpid());return 0;
}int main(void) {int fd = -1;fd = open(FILE_NAME, O_RDWR|O_CREAT, 00666);if (fd < 0) {fprintf(stderr, "open file %s failed! reason: %s\n", FILE_NAME, strerror(errno));exit(-1);}// 1.F_WRLCKint ret = flock_set(fd, F_WRLCK); // 写锁//int ret = flock_set(fd, F_WRLCK); // 读锁if (-1 != ret) {getchar();// 2.F_UNLCKflock_set(fd, F_UNLCK);getchar();}close(fd);return 0;
}
用以上的代码编译一个读锁程序,编译一个写锁程序:
main函数中替换以下代码进行编译
int ret = flock_set(fd, F_WRLCK); // 写锁
int ret = flock_set(fd, F_WRLCK); // 读锁
gcc file_lock.c -o file_w_lock
gcc file_lock.c -o file_r_lock
1. 设置锁使用:F_SETLK
情况一:读锁 和 读锁

结论:可以看出,两个程序都可以对文件进行加读锁!
情况二:读锁 和 写锁

结论:当文件上读锁后,就无法再给文件上写锁,且也无法检测处文件上锁状态;
即(./file_w_lock)执行代码 int ret = fcntl(fd, F_GETLK, &fflock); 后,其 fflock.l_type != F_UNLCK ,所以没法做中断操作;下面再对文件进行上锁操作,就函数返回-1了!
情况三:写锁 和 读锁

结论:文件上写锁后,再给文件上读锁,可以被检测出来文件已经上锁了,程序就返回结束了
情况四:写锁 和 写锁

结论:文件上写锁后,再给文件上写锁,可以被检测出来文件已经上锁了,程序就返回结束了
2. 设置锁使用:F_SETLKW
使用F_SETLKW会有等待阻塞的情况!
情况一:读锁 和 读锁

结论:可以看出,两个程序都可以对文件进行加读锁!
情况二:读锁 和 写锁

先给文件上读锁,再个文件上写锁,此时上写锁程序在获取文件的上锁状态时,被阻塞在此等待;

当我们在程序1那里按下回车键后,对文件解锁;右边程序就可以正常对文件上写锁了;否则会一直阻塞。。。
情况三:写锁 和 读锁

文件上写锁后,再给文件上读锁,可以被检测出来文件已经上写锁了,程序就返回结束了
情况四:写锁 和 写锁

结论:文件上写锁后,再给文件上写锁,可以被检测出来文件已经上写锁了,程序就返回结束了
3. 其他情况
这里还有另一种情况,就是程序1对文件上锁,程序2没有对文件上锁,然后对文件做写入操作
程序1还是上面的程序代码;
程序2代码如下:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>#define FILE_NAME "flock_demo.txt"int main(void) {int fd = -1;char buf[64] = "write file data...\n";fd = open(FILE_NAME, O_RDWR|O_CREAT, 00666);if (fd < 0) {fprintf(stderr, "open file %s failed! reason: %s\n", FILE_NAME, strerror(errno));exit(-1);}int ret = write(fd, buf, sizeof(buf));if (-1 == ret) {fprintf(stderr, "write failed. reason: %s\n", strerror(errno));exit(-2);}printf("write successful!\n");close(fd);return 0;
}
gcc file_write.c -o file_write
文件上读锁:

程序1给文件上读锁后,程序2依然可以 对文件做写入操作!
文件上写锁:

程序1给文件上写锁后,程序2依然可以对文件做写入操作!
结论:
由以上两个测试可知,给文件上锁,仅仅对相应也给文件上锁的程序可以限制,对于没有对文件上锁的程序无任何限制!
所以,在开发中,就得做出规定,大家对文件操作时都得对文件上锁后再进行操作!
总结
文件上锁的几种情况已经列举出来了,使用时注意一下就行!
相关文章:
Linux 文件锁 - fcntl
什么是文件锁? 即锁住文件,不让其他程序对文件做修改! 为什么要锁住文件? 案例,有两个程序,都对一个文件做写入操作。 #include <unistd.h> #include <stdio.h> #include <stdlib.h> …...
CellularAutomata元胞向量机-2-初等元胞自动机MATLAB代码分享
%% 二维元胞自动机%imagesc(a)的色度矩阵a中0->256由蓝变黄% 规则,先把中间点置为1,每一时间每一点如果%周围八个点和为偶数,则变为0,为奇数则变为 1% 颜色控制clc, clearMap [1 1 1; 0 0 0];% [0 0 0] is black, [1 1 1] is …...
OpenStack云平台搭建(6) | 部署Neutron
目录 1.在控制节点登录数据库配置 2.要创建服务证书,完成这些步骤 3.创建网络服务API端点: 4.安装网络组件 5.配置neutron组件 6.配置 Modular Layer 2 (ML2) 插件 7.配置Linuxbridge代理 8.配置DHCP代理 9.配置元数据代理 10.编辑/etc/nova/no…...
Lesson 05.Configuring the Oracle Network Environment
Lesson 05. Configuring the Oracle Network Environment 文章目录Lesson 05. Configuring the Oracle Network Environment1. 监听程序的配置文件有哪些,如何命名,保存在什么位置?2. Oracle 网络的服务名称文件是如何命名的,需要…...
理论五:接口vs抽象类的区别,如何用普通的类模拟抽象类和接口
在面向对象编程中,抽象类和接口是两个经常被用到的语法概念,是面向对象四大特性,以及很多设计模式、设计思想、设计原则编程实现的基础。比如,我们可以使用接口来实现面向对象的抽象特性、多态特性和基于接口而非实现的设计原则,使用抽象类来实现面向对象的继承特性和模板设计模…...
【Hello Linux】 Linux的权限以及Shell原理
作者:小萌新 专栏:Linux 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:介绍Linux的基础命令 Linux的权限以及Shell原理Shell的运行原理权限Linux中权限的概念如何切换用户如何提升当前操作的权限如何添加信任…...
【STM32】【HAL库】遥控关灯2 分机
相关连接 【STM32】【HAL库】遥控关灯0 概述 【STM32】【HAL库】遥控关灯1主机 【STM32】【HAL库】遥控关灯2 分机 【STM32】【HAL库】遥控关灯3 遥控器 需求 接收RF433和红外信号,根据信号内容控制舵机 硬件设计 主控采用stm32F103c6 STM32 433接收 其他接口 软件设计 接…...
代码随想录算法训练营第27天|● 93.复原IP地址 ● 78.子集 ● 90.子集II
93.复原IP地址 看完题后的思路 典型分割问题略lue略剪枝条件 sub: 1) 不是一位首字母为0 2)大于三位 3)介于0-255之间 4) 当已分割得到3个时,第四个直接从startIndex到末尾就行 代码 ArrayList<String> slist…...
Unity UI合批的问题
今天看到一个问题,主要说的是Unity中的UI资源合批的问题之前一直以为主要和UI资源在Hierarchy中的排列顺序有关,但其实这并不是最主要的,因为Unity会对同一个Canvas下的UI进行排序(注:不同Canvas下的资源是不能够合批的…...
MWORKS--系统建模与仿真
MWORKS--系统建模与仿真1 系统定义特征2 系统研究2.1 特点与原则2.2 方法百度百科归纳同元杠归纳3 系统建模与仿真3.1 系统、模型、仿真的关系3.2 系统建模4 建模方法4.1 方法4.2 一般流程4.3 目的5 仿真方法5.1 方法5.2 流程参考1 系统定义 系统是由相互作用相互依赖的若干组…...
PC端开发GUI
PC端开发GUI 一、搭建PC端环境:常规方式1、Python2、Pycharm二、搭建PC端环境:创建虚拟环境1、创建文件夹存放虚拟环境相关2、配置环境变量3、创建.ui文件4、.ui文件转成.py文件5、打包.py文件来发布.exe一、搭建PC端环境:常规方式 1、Python 注意Python版本不能超过3.9,…...
解读手机拍照的各个参数(拍照时,上面会有6个符号)
1第一个符号是闪光灯符号,如下图所示。有四种模式, 手机的闪光灯分别为关闭、自动、开启和常亮四种状态。 关闭:就是在任何情况下都不会闪光 自动:由手机来判断此时的光线强弱,若手机测光认为光线太弱,则…...
数字钥匙最新进展文章
在未来出行上,智能汽车越来越卷。 新车除了拼高精度激光雷达、堆大算力芯片、标配辅助驾驶、智能语音识别,还在车钥匙上展开了激烈角逐,越来越多的厂商开始在量产车型上搭载数字钥匙,实现无钥匙进入车内。 去年1月蔚来发布轿车E…...
如何在VMware虚拟机上安装运行Mac OS系统(详细图文教程)
一、安装前准备 虚拟机运行软件:VMware Workstation Pro,版本:16.0.0 。VMware Mac OS支持套件:Unlocker。Mac OS系统镜像。 如果VMware 在没有安装Unlocker的情况下启动,在选择客户机操作系统时没有支持Mac OS的选项…...
C++中的强制类型转换
接触过C语言的朋友都知道,C语言中也有强制类型转换,但是C语言中的强制类型转换会有一些问题,比如: int a 0x1234; char b (char)a; 上述的代码出现一个问题就是a 这个int型强制转化成b 这个char型时损失了一些精度,…...
任何人都可以学习Rasa之优秀Rasa学习资源推荐
任何人都可以学习Rasa之优秀Rasa学习资源推荐 欢迎同学们报名Gavin老师的Rasa系列课程,任何人都可以学习Rasa之优秀Rasa学习资源推荐: 1.NLP on Transformers高手之路137课 2 .Rasa 3.X 智能对话机器人案例开发硬核实战高手之路 (7大项目Ex…...
数据中心的 TCP-Delay ACK 与 RTO, RACK
TCP 对 RTO 有个最小值限制,一般限制为 MIN_RTO 200ms。之所以有这个限制,在于要适应 Delay ACK,而 Delay ACK 的意义,不多说,摘自 RFC1122: MIN_RTO 应该足够大,以覆盖 Delay ACK 的影响&…...
MySQL与常见面试题
目录 事务概述ACIDAUTOCOMMIT总结并发一致性问题丢失修改读脏数据不可重复读幻读原因和解决方法隔离级别未提交读(READ UNCOMMITTED)提交读(READ COMMITTED)可重复读(REPEATABLE READ)可串行化(SERIALIZABLE)加锁封锁粒度封锁类型读写锁意向锁...
FFmpeg进阶: 采用音频滤镜对音频进行转码
文章目录采样位数采样率声道布局码率使用FFmpeg音频滤镜进行转码参考链接很多时候为了让视频文件适应不同的播放领域,我们需要对音频文件进行转码操作,转码操作其实主要就是修改音频文件的各种参数包括:采样位数、采样率、音频布局、码率等等。下面分别介…...
C++:AVL树
AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下,时间复杂度为O(N); 两位俄罗斯的数学家G.M.Ade…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
