飞凌嵌入式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、当一个文件描述符被复制时,会共用同一个文件锁。

相关推荐
南清的coding日记2 小时前
从零开始学习微调简历分析大模型01 - LLaMA-Factory 扫盲
学习·语言模型
Predestination王瀞潞2 小时前
Java EE开发技术 (报错解决 请求的资源[/Bank/$%7BpageContext.request.contextPath%7D/login]不可用)
java·java-ee
Sahadev_2 小时前
GitHub 一周热门项目速览 | 2025年12月08日
java·大数据·人工智能·安全·ai作画
BlackWolfSky2 小时前
ES6 学习笔记3—7数值的扩展、8函数的扩展
前端·javascript·笔记·学习·es6
黑客思维者2 小时前
Python 3.14(2025最新版)的核心语法特性分析
服务器·开发语言·python·多线程
Jaxson Lin2 小时前
Java编程进阶:打造专属于你的背单词软件V1.0
java·开发语言
7ioik2 小时前
什么是栈帧?
java
路边草随风2 小时前
java实现发布flink k8s application模式作业
java·大数据·flink·kubernetes
whltaoin2 小时前
【Java SE】Java IO 类常用方法大全:从字节流到 NIO 的核心 API 汇总
java·开发语言·api·nio