飞凌嵌入式ElfBoard-文件I/O的深入学习之文件锁

前面有讲过竞争冒险的问题,如果有多个进程对文件进行I/O操作,容易产生竞争状态、导致文件中的内容与预想的不一致的问题,由此引入文件锁。

内核提供的锁机制用于对共享资源的访问进行保护,而文件锁是一种应用于文件的锁机制,当多个进程同时操作同一文件时,对文件上锁,来避免多个进程同时操作同一文件时产生竞争状态。

文件锁可以分为建议性锁和强制性锁两种:

建议性锁本质上是一种协议,程序访问文件之前,先对文件上锁,上锁成功之后再访问文件,这是建议性锁的一种用法。顾名思义,在没有对文件上锁的情况下直接访问文件,也是可以访问的。

强制性锁比较好理解,它是一种强制性的要求,如果进程对文件上了强制性锁,其它的进程在没有获取到文件锁的情况下是无法对文件进行访问的。强制性锁会让内核检查每一个I/O操作验证调用进程是否是该文件锁的拥有者,否则将无法访问文件。当一个文件被上锁进行写入操作的时候,内核将阻止其它进程对其进行读写操作。采取强制性锁对性能的影响很大,每次进行读写操作都必须检查文件锁。

flock用于对文件加锁或者解锁,但是只能产生建议性锁,并且同一个文件不会同时具有共享锁和互斥锁。

1.头文件

#include <sys/file.h>

2.函数原型

int flock(int fd, int operation);

3.参数

fd:表示需要加锁文件的文件描述符。

operation:指定了操作方式,可以设置为以下值的其中一个:

LOCK_SH:在fd引用的文件上放置一把共享锁。所谓共享,指的便是多个进程可以拥有对同一个文件的共享锁,该共享锁可被多个进程同时拥有。

LOCK_EX:在fd引用的文件上放置一把排它锁(或叫互斥锁)。所谓互斥,指的便是互斥锁只能同时被一个进程所拥有。

LOCK_UN:解除文件锁定状态,解锁、释放锁。

除了以上三个标志外,还有一个标志:

LOCK_NB:表示以非阻塞方式获取锁。默认情况下,调用flock无法获取到文件锁时会阻塞、直到其它进程释放锁为止,如果不想让程序被阻塞,可以指LOCK_NB标志,如果无法获取到锁,应立刻返回(错误返回,并将errno设置为EWOULDBLOCK),通常与LOCK_SH或LOCK_EX一起使用,通过位或运算符组合在一起。

4.返回值

成功将返回 0,失败返回-1,并会设置errno。

5.示

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/file.h> #include <signal.h> static int fd; char *name; static void sig_handler(int sig){ if (sig != SIGUSR1) return; flock(fd,LOCK_UN); close(fd); printf("%s is unlock\n",name); exit(0); } int main(int argc, char *argv[]) { if (argc != 2) { printf("usage:flocktest filename\n"); return -1; } fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777); if (fd < 0) { perror("open file"); return -1; } if (flock(fd, LOCK_EX | LOCK_NB) < 0) { perror("flock file"); close(fd); return -1; } name = argv[1]; printf("flock file %s succeed\n", name); signal(SIGUSR1, sig_handler); while (1) sleep(1); return 0; } |

以上先通过传参获取要打开的文件,并对文件进行加锁操作,然后设置当接收到SIGUSR1信号时,解锁并结束进程,未收到信号则一直循环等待。

另一个进程:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/file.h> #include <signal.h> #include <string.h> int main(int argc, char *argv[]) { int pid, fd; char *name; char buf[30] = "flock2 test"; char buf2[30]; if (argc != 3) { printf("usage:flocktest filename pid\n"); return -1; } pid = atoi(argv[2]); fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777); if (fd < 0) { perror("open file"); return -1; } if (flock(fd, LOCK_EX | LOCK_NB) < 0) { perror("第一次加锁失败"); if (write(fd, buf, strlen(buf)) < 0) { perror("flock2 write"); exit(-1); } if (lseek(fd, 0, SEEK_SET) < 0) { perror("lseek error"); exit(-1); } if (read(fd, buf2,strlen(buf)) < 0) { perror("read error"); exit(-1); } if (strcmp(buf,buf2) == 0) { printf("读写相同\n"); } kill(pid,SIGUSR1); sleep(1); if (flock(fd, LOCK_EX | LOCK_NB) < 0) perror("第2次加锁失败"); else printf("第2次加锁成功\n"); } return 0; } |

在此程序中,同样对传参指定的文件进行加锁操作,当加锁失败后,先写入特定内容,然后读取对比,可以看到下面实际运行时,打印信息"读写相同",说明虽然有锁,但是仍然可以进行IO操作。而后通过kill向传参指定的上一个进程的pid发生SIGUSR1信号,使flock1进程触发解锁以及退出进程操作。而后flock2再次进行加锁,可以看到打印信息中"第2次加锁成功"。

6)编译运行并查看测试结果

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ./flock1 test \& //flock1是第一个例程的可执行程序,test是任意文件 \[1\] 5303 //后台运行第一个程序时,返回进程pid flock file test succeed ./flock2 test 5303 //flock2是第二个例程,test是指定的文件,5303是第一个程序的pid 第一次加锁失败:Resource temporarily unavailable 读写相同 test is unlock 第2次加锁成功 [1]+ 已完成 ./flock1 test //后台运行的第一个程序结束退出 |

有几点需要注意:

1、同一进程对文件多次加锁不会导致死锁。

2、文件关闭的时候,会自动解锁。

3、一个进程不可以对另一个进程持有的文件锁进行解锁。

4、由fork创建的子进程不会继承父进程所创建的锁。

5、当一个文件描述符被复制时,会共用同一个文件锁。

相关推荐
用户2190326527356 小时前
能省事”。SpringBoot+MyBatis-Plus:开发效率提升10倍!
java·spring boot·mybatis
CheungChunChiu6 小时前
Linux 图形栈全景解析:从 OpenGL 到 DRM/KMS 的完整链路
linux·运维·服务器·opengl
爱睡觉的王宇昊6 小时前
PCB设计完全指南:从软件选择到基础规范(通用电路篇详解)
笔记·stm32·单片机·嵌入式硬件·学习
小楼v6 小时前
构建高效AI工作流:Java生态的LangGraph4j框架详解
java·后端·工作流·langgraph4j
m5655bj6 小时前
通过 Python 提取 PDF 表格数据
服务器·python·pdf
一个响当当的名号6 小时前
机械硬盘如何工作
学习
白帽子凯哥哥6 小时前
2026零基础如何参与护网行动?(非常详细)
数据库·sql·学习·漏洞·xss
jvstar6 小时前
JNI 面试题及答案
java
天呐草莓6 小时前
部署 Vue 项目到阿里云云服务器
服务器·前端·vue.js
虾说羊6 小时前
JVM 高频面试题全解析
java·开发语言·jvm