fcntl函数讲解

前言

fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性

函数原型:

#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd ,struct flock* lock);int fcntl(int fd, int cmd, ... /* arg */ );

(经常用这个fcntl函数改变非阻塞)

参数:
fd:文件描述符
cmd:设置的命令
F_GETFL(常用)
F_SETFL(常用)
arg:可有可无,由第二个参数决定,比如get时候没有,set时候有值
返回值:
文件状态标志
-1 :失败

步骤:
int flag;
flag = fcntl(sockfd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, flag);

若你想设置回阻塞,只需要把Flags改为iFlags&~O_NONBLOCK即可。



另外一个常用的作用就是使用文件锁

概述

在多数unix系统中,当多个进程/线程同时编辑一个文件时,该文件的最后状态取决于最后一个写该文件的进程。

对于有些应用程序,如数据库,各个进程需要保证它正在单独地写一个文件。这时就要用到文件锁。

文件锁(也叫记录锁)的作用是,当一个进程读写文件的某部分时,其他进程就无法修改同一文件区域。

能够实现文件锁的函数主要有2个:flock和fcntl

早期的伯克利版本只支持flock,该函数只能对整个文件加锁,不能对文件的一部分加锁。

fcntl是在flock基础上构造的函数,它提供了一个简化的接口。它们允许对文件中任意字节区域加锁,短至一个字节,长至整个文件。

int fcntl(int fd, int cmd ,struct flock* lock);

成功则返回0,若有错误则返回-1,错误原因存于errno。

函数传入值cmd
F_DUPFD:复制一个现存的描述符
F_GETFD:获得fd的close-on-exec(执行时关闭)文件描述符标志,若标志未设置,则文件经过exec()函数之后仍保持打开状态
F_SETFD:设置close-on-exec 标志,该标志由参数arg 的FD_CLOEXEC位决定
F_GETFL:得到open设置的标志
F_SETFL :改变open设置的标志
F_GETLK:根据lock参数值,决定是否可以上文件锁
F_SETLK:设置lock参数值的文件锁

这里我们要了解一个结构体:

struct flock {
short l_type;/* one of F_RDLCK, F_WRLCK, F_UNLCK /
short l_whence;/
SEEK_SET, SEEK_CUR, SEEK_END /
off_t l_start;/
offset in bytes, relative to l_whence /
off_t l_end;/
length, in bytes, 0 means lock to EOF /
off_t l_pid;/
returned with F_GETLK */
};
在这里插入图片描述

其中,

  1. 锁类型:共享读锁F_RDLCK,独占性写锁F_WRLCK,解锁F_UNLCK
  2. 加锁或解锁区域的起始字节偏移量(l_start, l_whence)
  3. 区域字节长度(L_len)
  4. 进程的id持有的锁能阻塞当前进程,仅由F_GETLK返回
  5. 锁可以在文件尾处开始或者越过尾端开始,但是不能在文件起始位置之前开始
  6. 若l_len=0, 表示锁的范围可以扩大到最大可能偏移量,这意味着不管向文件中追加多少数据,它们都可以处于锁的范围内,而且起始位置可以任意
  7. 设置l_start和l_whence指向文件的起始位置,并且指定l_len=0,以实现对整个文件加锁(一般l_start=0, l_whence=SEEK_SET)

重点了解一下cmd的方式:

  1. F_GETLK:判断由flockptr所描述的锁是否会被另一把锁所排斥(阻塞),如果存在一把锁阻止创建由flockptr所描述的锁,由该现有锁的信息将重写flockptr指向的信息。如果不存在这种情况,则除了将l_type设置为F_UNLCK之处,flockptr所指向结构中的其他信息保持不变
  2. F_SETLK:设置由flockptr所描述的锁,如果程序试图获得一把锁,而系统阻止程序获得该锁,则fcntl会立即返回错误,errno设置为EACCES或EAGAIN。当l_type=F_UNLCK时,此命令用来清除指定的锁
  3. F_SETLKW:F_SETLK的阻塞版本(wait)。如果程序尝试获得的锁无法被授予,调用进程会进入休眠直到进程获得锁或者信号中断

注意:用F_GETLK 测试能否创建一把锁,然后用F_SETLK尝试建立锁之间并非原子操作,也就是说两次调用之间有可能另一进程插入并创建了相同的锁。如果不希望在等待锁变为可用时产生阻塞,就必须处理由F_SETLK返回的可能出错值

我们要清楚这个操作不是原子性的,也就是说在用F_GETLK 测试能否创建一把锁,然后用F_SETLK尝试建立锁之间,两次调用之间有可能另一进程插入并创建了相同的锁。所以我们都必须要处理一下由F_SETLK返回的值。

要点:

  1. 任意多个进程在一个给定的字节上可以有一把共享的读锁(F_RDLCK),但是在一个给定的字节上只能有一个进程有一把独占性写锁(F_WRLCK)
  2. 如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁,如果在一个字节上已经有一把独占性写锁,则不能再对它加任何读锁
  3. 对于单个进程而言,如果进程对某个文件区域已经有了一把锁,然后又试图在相同区域再加一把锁,则新锁会替换旧锁
  4. 加读锁时,该描述符必须是读打开,加写锁时,该描述符必须是写打开


不错的博客,请点击!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注