fcntl函数有5种功能:

  1. 复制一个现有的描述符(cmd=F_DUPFD).
  2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
  3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
  4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
  5. 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).

  这是一种读写锁的扩展类型,他可用于有亲缘关系或无亲缘关系的进程之间共享某个文件的读或写,被锁住的文件通过文件描述符访问,执行上锁的操作时fcntl,这种类型的锁通常在内核中维护其属主是由属主的进程ID标识,这就说明了锁可用于不同进程之间上锁,而不是统一进程内的不同线程上锁。

  这是Unix内核提供的上锁特性——记录上锁,应用程序会指定文件中待上锁或解锁的部分字节范围,这个字节范围会跟同一文件内一个或多个逻辑记录有关。

  posix记录上锁制定了一个特殊的字节范围以指定整个文件,他的偏移为0(文件的开头),长度也为0.

  粒度指的是被锁住的对象的大小,对于posix记录上锁来说,粒度就是单个字节,通常情况下粒度越小,允许同时访问的用户数也就越多,fcntl仅仅是个库函数而不是系统调用,使用fcntl完成lockf的实现

  文件记录锁是fcntl函数的主要功能。记录上锁粒度是整个文件大小时,是文件上锁

  记录锁:实现只锁文件的某个部分,并且可以灵活的选择是阻塞方式还是立刻返回方式

#include<fcntl.h>int fcntl(int fd, int cmd ,.../*struct flock* arg*/);
//返回:成功取决与cmd,失败返回-1

  fcntl的返回值:  与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。第一个返回新的文件描述符,第二个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。

  当fcntl用于管理文件记录锁的操作时,第三个参数指向一个struct flock *lock的结构体

struct flock
{short l_type;/*锁的类型F_RDLCK:为了获得一把共享锁文件必须以“读”或“读/写”方式打开。F_WRLCK:为了获得一把写(独占)文件也必须以“读”或“读/写”方式打开。F_UNLCK:它用于把一个锁定的区域解锁*/short l_whence;  /*偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END*/off_t l_start;     /*加锁的起始偏移*/off_t l_len;    /*上锁字节即从偏移开始的连续字节数,o means until end-of-file即从起始偏移到文件偏移的最大值*/pid_tl_pid;   /*锁的属主进程ID,pid returned by F_GETLK */
};

  跟lseek一样,起始字节偏移是作为一个相对偏移(l_start成员)伴随解释(l_whence成员)指定的,l_whence成员指定的三个值

  1. SEEK_SET:l_start相对与文件的开头解释
  2. SEEK_CUR:l_start相对于文件的当前字节偏移(即当前读写指针位置)解释
  3. SEEK_END:l_start相对于文件的末尾解释

  锁住整个文件:

  1. 指定l_whence成员为SEEK_SET,l_start为0,l_len为0
  2. 使用lseek把读写指针定位到文件的头,然后指定l_whence成员为SEEK_CUR,l_start为0,l_len为0

 cmd:

  1. F_DUPFD:与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。如果对象是文件(file)的话,返回一个新的描述符,这个描述符与arg共享相同的偏移量(offset),新的文件描述符与原来的文件描述符操作符一样的某对象的引用,相同的访问模式(读,写或读/写)  ,相同的文件状态标志(如:两个文件描述符共享相同的状态标志);原来的文件描述符与新的文件描述符结合在一起的close-on-exec标志被设置成交叉式访问execve(2)的系统调用
  2. F_GETFD:读取文件描述符close-on-exec标志,
  3. F_SETFD:将文件描述符close-on-exec标志设置为第三个参数arg的最后一位类似FD_CLOEXEC.如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(),否则如果通过exec运行的话,文件将被关闭(arg被忽略)
  4. F_GETFL:获取文件打开方式的标志;当执行F_SETLK时fcntl函数返回一个错误时,导致该错误的某个锁的信息可能由F_GETLK命令返回,从而允许我们确定哪个进程锁着了请求的文件区,及上锁的方式,但是也可返回该文件区已经解锁的信息,因为在F_SETLK和F_GETLK之间该文件可能解锁。
  5. F_SETF:设置文件打开方式为arg指定方式,O_DIRECT :最小化或去掉reading和writing的缓存影响.系统将企图避免缓存你的读或写的数据.如果不能够避免缓存,那么它将最小化已经被缓存了的数 据造成的影响.如果这个标志用的不够好,将大大的降低性能;O_ASYNC:当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候
  6. F_SETLK (F_GETLK,F_SETLK,F_SETLKW当使用这些命令时,fcntl的第三个参数必须是一个指向flock结构的指针):此时fcntl函数用来设置或释放锁。当short_l_type为F_RDLCK为加读锁,F_WDLCK为加写锁,F_UNLCK为解锁。如果锁被其他进程占用,则返回-1;这种情况设的锁遇到锁被其他进程占用时,会立刻停止进程。
  7. F_SETLKW:此时也是给文件上锁,不同于F_SETLK的是,当无法将所请求的锁授予调用进程,调用线程箭镞色到该锁能够授予为止,该命令会等待相冲突的锁被释放。
  8. F_GETLK:检查由arg指向的锁以确定是否由某个已存在的锁会妨碍将新锁授予调用进程,如果没有这样的锁存在,arg指向的flock结构的l_type成员就被设置为F_UNLCK,否则,关于这个已存在的锁的信息将在由arg指向的flock结构中返回也就是该结构的内容由fcntl复写,包括持有该锁的进程ID,。如果存在一个或多个锁与希望设置的锁相互冲突,则fcntl返回其中的一个锁的flock结构。
  9. F_SETOWN,F_GETOWN:设置获取异步I/O所有权(SIGIO,SIGURG)0;s设置当前进程id时如果参数为负,表示该值的绝对值的一个进程组id;取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回成负值(arg被忽略)

注意:

  1. 多进程情况下:每个进程可以在该字节区域上设置不同的读锁。但给定的字节上只能设置一把写锁,并且写锁存在就不能再设其他任何锁,且该写锁只能被一个进程单独使用
  2. 单个进程时:文件的一个区域上只能有一把锁,若该区域已经存在一个锁,再在该区域设置锁时,新锁会覆盖掉旧的锁,无论是写锁还时读锁
  3. 一个进程可以对文件的某个特定的字节范围多次发送F_SETLK or F_SETLKW,每次是否成功取决于其他进程当时是否锁住该字节范围及锁的类型,与本进程先前是否锁着该字节范围无关,也就是说F_SETLC or F_SETLKW会覆盖先前执行同一字节的两个命令,文件能否读写相应的记录与其他进程锁无关(劝告性上锁)也就是说,一个进程可能访问已经被另一个进程独占的锁朱文件的记录,但彼此协作的进程应该不去访问
  4. 调用进程已有的针对同一字节的锁不会妨碍它获取新锁,因为同一进程内,后执行的锁的命令会覆盖先前的命令。例,对一个进程的同一字节范围先后执行F_SETLK(l_type=F_WRLCK)和F_GETLK(l_type=F_RDLCK),这两个命令间无其他进程的干扰,且他们都执行成功,那么F_GETLK返回l_type成员是F_UNLCK
  5. 针对同一文件的任意字节,最多有一把类型的锁(读入或写出锁),而且给定的一个字节可以有多个多读出锁但只能有一个写入锁;当一个文件不是打开读的,为其请求读出锁会失败,写入锁同理
  6. 打开文件的进程来说,文件描述符关闭或进程终止,与文件关联的锁都被删除
  7. 锁不能由fork子进程继承(甚至于所有关闭的文件描述符先前是在其文件已由本进程(通过该文件的另一个描述符)上锁后才打开也不例外,删除锁的关键是在于进程ID,而不是引用同一个文件的描述符数目及打开的目的,所以锁和进程ID密切相关所以他不能由fork继承也顺理成章)

空锁

  如果一个进程实际并未对一个区域进行锁定,而调用解锁操作也会成功,但是它并不能解其他的进程加在同一区域上的锁。也可以说解锁请求最终的结果取决于这个进程在文件中设置的任何锁,没有加锁,但对其进行解锁得到的还是没有加锁的状态。

代码github连接:https://github.com/tianzengBlog/test/tree/master/ipc2/fcntl

复制文件描述符

fcnlt(oldfd, F_DUPFD, 0) ==dup2(oldfd, newfd)

关闭close_on_exec

 fcntl(fd, F_SETFD, 0);//关闭fd的close-on-exec标志

设文件描述符状态

  fcntl可将一个socket设置成非阻塞模式,在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。

flags = fcntl(sockfd, F_GETFL, 0); //获取文件的flags值。
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //设置成非阻塞模式;
flags  = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK); //设置成阻塞模式;

fcntl,flock.lockf区别

  Posix locks: fcntl/lockf BSD locks: flock
范围 字节范围锁 只能对整个文件加锁
类型 建议锁(默认)/
强制锁(非 POSIX 标准,默认关闭)
建议锁
关系关联 与进程关联( 标准 POSIX )/
与文件描述符关联(非 POSIX 标准,
需特定参数,
linux 3.15 支持)
与文件描述符关联
网络文件系统 支持 NFS 
不支持 ocfs2
支持 NFS(实现仿 fcntl,
linux 2.6.12 支持)
支持 ocfs2

劝告锁

  共含义是内核维护着已由各个进程上锁的所有文件的正确信息,他不能防止一个进程写由另一个进程读锁定的某个文件,也不能防止一个进程读已由另一个进程写锁定的文件。一个进程能够无视劝告性锁而写一个读锁文件,或读一个写锁文件,前提时该进程有读或写该文件的足够权限。

  劝告性上锁对于协作进程足够。如守护进程,这些程序访问如序列号文件的共享资源,而且都在系统管理员的控制下,只要包含该序列号的真正文件不是任何进程都可写,那么在该文件被锁住期间,不理会劝告性锁的进程随意进程无法访问写它。

强制锁

  对于强制性上锁,内核检查每个read和write请求,以验证其操作不会干扰由某个进程持有的某个锁,对于通常的时阻塞描述符,与某个强制性锁冲突的read和write将把调用进程投入睡眠,直到该锁释放为止,对于非阻塞描述符,与某个强制锁冲突的read和write将导致他们返回一个EAGAIN

  为对某个特定的文件实行强制锁应满足:

  1. 组成员执行位必须关掉
  2. SGID位必须打开

  打开某个文件的SUID位而不打开他的用户执行位是没有意义的,同样打开SGID位而不打开组成员的执行位也是没有意义的,因此这种方式的强制性上锁不会影响任何现有的用户软件,强制性上锁不需要新的系统调用

  1. linux 内核会阻塞其他进程的 IO 请求
  2. 可以通过删除锁文件绕过

与进程关联

  • 当一个进程终止时,所建立的所有锁全部被释放
  • 关闭一个文件描述符,会释放对该文件的所有锁,包括对其他指向相同文件的文件描述符加的锁
    • 同一进程打开多个文件描述符 fd1fd2
    • 对 fd1 加锁
    • 关闭 fd2
    • fd1 上的锁会被释放
  • fork 产生的子进程并不继承父进程所设置的锁
  • 在执行 exec 后,新程序可以继承原程序的锁(如果对fd设置了close-on-exec,则exec前会关闭fd,相应文件的锁也会被释放)

与文件描述符关联

  1. 当一个文件描述符及其所有副本(包括子进程继承的和 dup 的)关闭时,才会释放对其建立的锁
  2. fork 的子进程由于继承了文件描述符,所以也继承了其上的锁
  3. 子进程对继承的文件描述符上的锁进行修改/解锁,会影响到父进程的锁(对于 dup 出的副本同样试用)
  4. 在执行 exec 后,新程序可以继承原程序的锁

fcntl/lockf和flock交互

  1. linux 2.0 后在本地文件系统上互不影响
  2. 在 NFS 上, flock 由于底层实现仿造 fcntl 的字节范围锁,所以两者会产生交互。

注意

  1. Linux fcntl 的强制锁在设置的时候会和 write/read 有 race condition。
  2. fcntl 有死锁检测,而 flock 没有
  3. linux 3.12 之前,在 NFS 上设置的 fcntl 锁会因为 client 长时间(90s)与 nfs server 失去连接而丢失

fcntl上锁文件

1.独占打开

/*************************************************************************> File Name: test.cpp> Author: Chen Tianzeng> Mail: 971859774@qq.com > Created Time: 2019年04月15日 星期一 08时33分04秒************************************************************************/#define LOCKFILE "/home/..."
/** 如果open成功,我们就持有相应的文件锁,函数返回前close原文件,不需要文件* 描述符因为文件的本身存在代表锁,至于他是否打开无关紧要,如果存在再次尝试* open*/
my_lock(int fd)
{int tfd;/** someone else has the lock,loop around and try again* *///如果文件不存在就创建他和独占打开它(O_EXCL),检查该文件的存在和创建必须是原子的while((tfd=open(LOCKFILE,O_RDWR|O_CREATE,O_EXCL,FILE_MODE))<0){if(errno!=EEXIST){cerr<<"open error for lock file: "<<strerror(errno)<<endl;exit(-1);}//opend the file,we have the lockclose(tfd);}
}
void mu_unlock(int fd)
{unlink(LOCKFILE);
}

  但存在问题:

  1. 如果进程持有的锁没有终止就释放它,那么文件名并未删除。解决办法:检查文件最近访问时间,大于某个确定的数量就删除;把进程持有的id写进锁文件,但是进程id在一段时间后会被重用。这种问题对于fcntl不成问题,因为它持有的锁会在进程终止时完全释放
  2. 如果另外的进程以打开文件,该进程只是无限的open,可以先sleep1s,然后再open。如果时fcntl指定FSETLKW命令,内核把进程投入睡眠,直到该锁可用,然后唤醒它
  3. 调用open和unlink删除一个额外的文件涉及系统访问时间,通常比fcntl调用两次(一次获取锁一次释放锁)花去的时间要长

2.如果新链接的名字已经存在,那么link将失败,为获取一个锁,首先创建一个唯一的临时文件,其路径名是含有调用进程的id(如果不同进程中的线程以及同一进程内的线程都需要上锁,那么所含的是进程id和线程id的某种组合),然后以建立文件众所周知的路径名调用link函数创建这个临时文件的一个连接,如果创建成功,只需unlink众所周知的路径名就可以解锁,如果link失败返回EEXIST错误,调用线程就需重新尝试,这要求临时文件路径名和锁文件众所周知的路径名在同一个文件系统中,因为硬链接不能跨越文件系统。

3.如果打开文件已经存在,打开时指定了O_TRUNC标志,而且调用进程不具备写访问权限,那么open将返回个错误,为获取一个锁,我们在指定O_CREATE|O_WRONLY|O_TRUNC标志并置mode参数为0(打开的新文件不存在任何权限位)的前提下调用open,如果调用成功我们就持有该锁以后使用完之后只需unlink路径名,如果open失败调用EACCES错误,调用线程就必须重新尝试,但是这种做法在线程具有超级用户特权下不起作用

C++——fcntl相关推荐

  1. Python 标准库之 fcntl

    在 linux 环境下用 Python 进行项目开发过程中经常会遇到多个进程对同一个文件进行读写问题,而此时就要对文件进行加锁控制,在 Python 的 linux 版本下有个 fcntl 模块可以方 ...

  2. linux c编程之fcntl

    fcntl可实现对指定文件描述符的各种操作,其函数原型如下: int fcntl(int fd, int cmd, ... /* arg */ ); 其中,操作类型由cmd决定.cmd可取如下值: F ...

  3. 使用 fcntl 函数 获取,设置文件的状态标志

    前言 当打开一个文件的时候,我们需要指定打开文件的模式( 只读,只写等 ).那么在程序中如何获取,修改这个文件的状态标志呢?本文将告诉你如何用 fcntl函数 获取指定文件的状态标志. 解决思路 1. ...

  4. 文件控制 fcntl函数具体解释

    摘要:本文主要讨论文件控制fcntl函数的基本应用.dup函数能够拷贝文件描写叙述符,而fcntl函数与dup函数有着异曲同工之妙.而且还有更加强大的功能,能够获取或设置已打开文件的性质,操作文件锁. ...

  5. linux fcntl注销信号,fcntl · Linux C API 参考手册 · 看云

    ## fcntl ## 文件描述词操作 ### 相关函数 ### open,flock ### 表头文件 ### ``` #include #include ``` ### 定义函数 ### ``` ...

  6. linux python fcntl模块 程序加锁 简介

    python 中给文件加锁--fcntl模块 import fcntl 打开一个文件 ##当前目录下test文件要先存在,如果不存在会报错.或者以写的方式打开 f = open('./test') 对 ...

  7. linux socket 阻塞非阻塞设置 fcntl,F_GETFL,F_SETFL,flags

    1.获取文件的flags,即open函数的第二个参数: flags = fcntl(fd,F_GETFL,0); 2.设置文件的flags: fcntl(fd,F_SETFL,flags); 3.增加 ...

  8. linux fcntl 函数 文件描述符选项控制

    功能描述:根据文件描述词来操作文件的特性. #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); ...

  9. UNIX中的文件控制--fcntl()

    文件控制函数          fcntl -- file control LIBRARY          Standard C Library (libc, -lc) SYNOPSIS       ...

  10. No module named 'fcntl'

    No module named 'fcntl' import sysprint(sys.path) for pa in sys.path:# print(pa.replace("\\\\&q ...

最新文章

  1. springboot mysql行锁_SpringBoot基于数据库实现简单的分布式锁
  2. JS笔记(20): JS中的同步编程和异步编程
  3. 弱引用的用途:在底层C++对象被上层python脚本对象使用时(转)
  4. AI 如何推动双碳目标达成?施耐德电气这么说
  5. mysql为什么选innodb_为什么现在的MySQL都要使用innoDB引擎-Go语言中文社区
  6. 作业要求 20171130 每周例行报告
  7. 你知道别人怎么看你恢复它?
  8. JS中的六大数据类型 (笔记0)
  9. c语言程序设计实验指导实验报告,C语言程序设计实验指导及报告.doc
  10. c语言推箱子代码_C语言烂大街的东西都学不会!C语言多关卡推箱子制作教程
  11. Cocos2d-xJson数据解析(读取和写入)
  12. 三次样条曲线拟合算法c语言,关于三次样条曲线拟合法
  13. 风灵月影捆绑软件解决办法(修改器)
  14. 基于verilog的 PRBS编码
  15. Mac OS X,下载并安装ant
  16. 展讯平台 LCD(Mipi)移植步骤及问题归纳
  17. LiteOS通信模组教程04-深度剖析LiteOS的AT框架
  18. 叠积木/银河系英雄传说[NOI2002]题解
  19. 《长尾理论1.0》读书笔记
  20. C语言中的选择结构语句

热门文章

  1. visio素材:安防监控visio素材图库
  2. EOS合约开发 - 钱包篇
  3. java中读取配置文件的内容
  4. lch 儿童围棋课堂 初级篇2 (李昌镐 著)
  5. 2022年四方系统/四方支付系统部署实战,以及细节讨论,Gitee版本
  6. 不管是蓝牙耳机还是有线耳机长时间佩戴都是有危害的,这些问题不容小觑!
  7. 编译原理(龙书):第七章部分题目参考答案
  8. Ubuntu18.04 替换vscode字体
  9. java sjis_java乱码分析
  10. matlab视频分辨率更改