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…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...