扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

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

2025/12/08
457
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

前面有讲过竞争冒险的问题,如果有多个进程对文件进行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 unlockn",name);

exit(0);

}

int main(int argc, char *argv[])

{

if (argc != 2) {

printf("usage:flocktest filenamen");

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 succeedn", 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 pidn");

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

相关推荐