1、flock,lockf,fcntl之间区别

  先上结论:flock是文件锁,锁的粒度是整个文件,就是说如果一个进程对一个文件加了LOCK_EX类型的锁,别的进程是不能对这个文件加锁的。

  lockf是对fcntl的封装,这两个东西在内核上的实现是一样的。它们的粒度是字节,不同的进程可以对相同的文件不同字节加LOCK_EX类型的锁。

2、linux文件系统

  在详解锁的实现机制前,我们先来看一下linux文件系统的实现。

  相信大家都看过这样一副图。与进程相关的是文件描述符表,文件表和i-node都是系统级别的。当我们在进程中打开一个文件时,文件描述符里就会产生一个文件描述符表项与之对应,同样的系统内也会有文件句柄和相应的i-node,我们需要注意的是多个文件表项(同一个进程或不同进程)可以指向同一个文件句柄。

2、 flock锁的实现机制

  flock在实现上关联到的是文件描述符(上图中文件描述符部分),这就意味着如果我们在进程中复制了一个文件描述符,那么使用flock对这个描述符加的锁也会在新复制出的描述符中继续引用。我们可以写如下代码测试:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <wait.h>
#define PATH "/tmp/lock"
int main()
{
int fd;
pid_t pid;
fd = open(PATH, O_RDWR | O_CREAT | O_TRUNC, );
flock(fd, LOCK_EX);
printf("%d: locked!\n", getpid());
pid = fork();
if (pid == ) {
// fd = open(PATH, O_RDWR|O_CREAT|O_TRUNC, 0644);
flock(fd, LOCK_EX);
printf("%d: locked!\n", getpid());
exit();
}
wait(NULL);
unlink(PATH);
exit();
}

输出结果:

Sangfor:aCMP/acmp-fefcfe3a1674 ~/test o ./a.out
: locked!
: locked!

由结果可见,父进程已经持有互斥锁的情况下,子进程应该对文件加锁失败才符合我们的预期。但是子进程确加锁成功。原因就在于flock的实现是关联文件描述符。

子进程由父进程创建,子进程的文件描述符表和父进程的一模一样,在fork()子进程后,子进程本身就持有该文件的互斥锁。同样的道理,对文件描述符dup(), dup2()都会有这样的问题。怎么解决这个问题呢?

1、重新open这个文件,使用新的文件描述符

fd = open(PATH, O_RDWR|O_CREAT|O_TRUNC, );

我们将上述注释掉的代码打开,重新编译执行,输出结果如下:

Sangfor:aCMP/acmp-fefcfe3a1674 ~/test o ./a.out
: locked!

这次子进程没有能上锁,重新open这个文件会创建一个新的文件描述符与父进程进程而来的描述符是不相关的,所以就符合我们预期的效果。

另外要注意:除非文件描述符被标记了close-on-exec标记,flock锁和lockf锁都可以穿越exec,在当前进程变成另一个执行镜像之后仍然保留。

3、lockf的实现机制

  lockf的实现是关联到内核i-node的(上图内核部分),每次加锁都会在i-node节点上挂一个flock的结构:

  struct flock
{
short l_type;/*F_RDLCK, F_WRLCK, or F_UNLCK*/
off_t l_start;/*相对于l_whence的偏移值,字节为单位*/
short l_whence;/*从哪里开始:SEEK_SET, SEEK_CUR, or SEEK_END*/
off_t l_len;/*长度, 字节为单位; 0 意味着缩到文件结尾*/
pid_t l_pid;/*returned with F_GETLK*/
};

  对LOCK_EX类型的锁来说,内核中最多只有一份这样的数据,所以即使文件描述符是从父进程进程过来或dup()产生的,对同一个节点加锁都会失败。我们写如下代码测试一下:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <wait.h>
#define PATH "/tmp/lock" int main()
{
int fd;
pid_t pid;
fd = open(PATH, O_RDWR | O_CREAT | O_TRUNC, );
lockf(fd, F_LOCK, );
printf("%d: locked!\n", getpid()); pid = fork();
if (pid == ) {
lockf(fd, F_LOCK, );
printf("%d: locked!\n", getpid());
exit();
}
wait(NULL);
unlink(PATH);
exit();
}

执行结果:

Sangfor:aCMP/acmp-fefcfe3a1674 ~/test o ./a1.out
: locked!

这次我们没有对文件重新open,子进程就一直等在那里。完全符合我们的预期。这也印证了lockf的实现是内核中i-node相关的。

此外有一篇文章介绍建议性锁和强制性锁,这篇文章是以flock为例说明的,flock和lockf的加锁规则是一致的。需要了解加锁规则请参看:

强制性锁和建议锁

linux 文件锁flock,lockf,fcntl的更多相关文章

  1. linux文件锁flock【转】

    转自: https://www.cnblogs.com/kex1n/p/7100107.html linux文件锁flock   在多个进程同时操作同一份文件的过程中,很容易导致文件中的数据混乱,需要 ...

  2. Linux文件锁flock

    Linux文件锁flock 在多个进程同时操作同一份文件的过程中,很容易导致文件中的数据混乱,需要锁操作来保证数据的完整性,这里介绍的针对文件的锁,称之为“文件锁”-flock. flock,建议性锁 ...

  3. Linux文件锁学习-flock, lockf, fcntl

    参考  linux中fcntl().lockf.flock的区别 这三个函数的作用都是给文件加锁,那它们有什么区别呢? 首先flock和fcntl是系统调用,而lockf是库函数.lockf实际上是f ...

  4. Linux 文件锁flock 实现两个进程相互监听存活状态

    表头文件  #include<sys/file.h> 定义函数  int flock(int fd,int operation); 函数说明  flock()会依参数operation所指 ...

  5. Linux文件锁flock ,检测进程是否已经存在

    在多个进程同时操作同一份文件的过程中,很容易导致文件中的数据混乱,需要锁操作来保证数据的完整性,这里介绍的针对文件的锁,称之为“文件锁”-flock.  头文件:#include<sys/fil ...

  6. 文件锁-fcntl flock lockf

    这三个函数的作用都是给文件加锁,那它们有什么区别呢? 首先flock和fcntl是系统调用,而lockf是库函数.lockf实际上是fcntl的封装,所以lockf和fcntl的底层实现是一样的,对文 ...

  7. 每天进步一点点——Linux文件锁编程flock

    转载请注明出处:http://blog.csdn.net/cywosp/article/details/30083015 1. 场景概述     在多线程开发中.相互排斥锁能够用于对临界资源的保护,防 ...

  8. php原子操作,文件锁flock,数据库事务

    php原子操作,文件锁flock,数据库事务 php没有继承posix标准支持的unix锁,只封装了一个linux系统调用flock(信号量也能做成锁),按理也是可以使用锁机制的,虽然效率低一点.ph ...

  9. Linux文件锁【转】

    本文转载自:http://blog.csdn.net/dragon_li_chen/article/details/17147911 一.文件锁的分类: 翻阅参考资料,你会发现文件锁可以进行很多的分类 ...

随机推荐

  1. 【转】awk学习笔记

    Awk学习笔记 整理:Jims of 肥肥世家 <jims.yang@gmail.com> Copyright © 2004 本文遵从GPL协议,欢迎转载.修改.散布. 第一次发布时间:2 ...

  2. zeus部署

    1.下载zeus 阿里在github上已经不维护zeus了,在网上找到一个别人贡献的 https://github.com/michael8335/zeus2 下载下来 通过shell rz命令上传到 ...

  3. Redis 与 MQ 的区别

    Redis是一个高性能的key-value数据库,它的出现很大程度补偿了memcached这类key-value存储的不足.虽然它是一个数据库系统,但本身支持MQ功能,完全可以当做一个轻量级的队列服务 ...

  4. ADO.net 增删改查封装DBhelper

    using System; using System.Collections.Generic; using System.Data.SqlClient;//引用数据库客户端 using System. ...

  5. 小程序swiper组件的bindchange方法重复执行问题

    这是官方文档的说法给出了swiper组件一直来回滑动的bug原因 以下是修正方法 <swiper autoplay="{{autoplay}}" interval=" ...

  6. C语言常用库函数

    一.数学函数 调用数学函数时,要求在源文件中包下以下命令行: #include <math.h> 函数原型说明 功能 返回值 说明 int abs( int x) 求整数x的绝对值 计算结 ...

  7. php类知识---常量点滴

    常量在类的内部,外部都可以访问,要通过类名::常量名 常量没有访问修饰符 与变量不同的是,常量不能放在双引号中取值,而变量可以 <?phpclass mycoach{ public $name= ...

  8. javaScript高级程序设计第3版笔记

    <script src = "xxx.js" defer = "defer"> </script> 在引用外部script时,<s ...

  9. CF #366 DIV2 C. Thor 模拟 queue/stack降低复杂度

    C. Thor time limit per test 2 seconds memory limit per test 256 megabytes input standard input outpu ...

  10. HDU - 3586 Information Disturbing 树形dp二分答案

    HDU - 3586 Information Disturbing 题目大意:从敌人司令部(1号节点)到前线(叶子节点)的通信路径是一个树形结构,切断每条边的联系都需要花费w权值,现在需要你切断前线和 ...