一、fcntl函数

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd,.../*int arg*/);
  • 功能:​改变已经打开文件的属性
  • 参数:
  • 参数1:文件描述符
  • 参数2:相关的选项
  • 参数3(可选):总是一个整数,但是在记录锁部分,这个参数则是指向一个结构的指针
  • 返回值:
  • 若成功返回值依赖于cmd(见下)
  • 若出错为-1

二、fcntl的功能


 fcntl有5种功能

  • 复制​一个现存的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)
  • 获得/设置​文件描述符​标记(cmd = F_GETFD或F_SETFD)
  • 获得/设置​文件状态​标志(cmd = F_GETFL或F_SETFL)
  • 获得/设置​异步I/O​所有权(cmd = F_GETOWN或F_ SETOWN)
  • 获得/设置​记录锁(cmd = F_GETLK , F_SETLK或F_SETLKW),详细使用见文章:​​


F_DUPFD


复制文件描述符fd,新文件描述符作为函数值返回。它是尚未打开的各 描述符中大于或等于第三个参数值(取为整型值)中各值的最小值。新描述符与 fd共享同 一文件表项。但是,新描述符有它自己的一套文件描述符标志,其 FD_CLOEXEC文件描述符标志则被清除(这表示该描述符在exec时仍保持有效)

调用成功时的返回值:新的文件描述符


F_DUPFD_CLOEXEC

复制文件描述符,设置与新描述符关联的FD_CLOEXEC文件描述符标志的值,返回新文件描述符

F_GETFD


对应于fd的文件描述符标志作为函数值返回。当前只定义了一个文件描 述符标志FD_CLOEXEC

APUE编程:17---文件I/O之(fcntl函数)_#include

调用成功时的返回值:返回相应的标志


F_SETFD

对于fd设置文件描述符。新标志值按第3个参数(取为整型值)设置

  • 很多现有的与文件描述符标志有关的程序并不使用常量FD_CLOEXEC,而是将此标志设置为0(系统默认,在exec时不关闭)或1(在exec时关闭)

F_GETFL


对应于fd的文件状态标志作为函数值返回。在说明open函数时,已说明了文件状态标志(如下图所示)

APUE编程:17---文件I/O之(fcntl函数)_#include_02

重点:但是,前5个访问方式标志(O_RDONLY、O_WRONLY、O_RDWR, O_EXEC、O_SEARCH)并不各占1位(如前所述,历史原因,前3个标志的值分别是0、1、2)。这5个值互斥,一个文件的访问方式只能取这5个值之一。因此使用F_GETFL得到结果之后还需要与屏蔽字O_ACCMODE进行按位与取得访问方式位,然后将结果与这5个值中的每一个相比较

调用成功时的返回值:返回相应的标志


F_SETFL

将文件状态标志设置为第三个参数的值(取为整型值)。可以更改的几个标志是:O_APPEND、O_NONBLOCK、O_SYNC、O_DSYNC、O_RSYNC、O_FSYNC、O_ASYNC.

F_GETOWN


返回当前接收SIGIO和SIGURG信号的进程ID或进程组ID

返回值:如果为正值,代表得到进程ID。如果为负值(-1以外),代表得到进程组ID


F_SETOWN


设置接收SIGIO和SIGURG信号的进程ID或进程组ID

参数:正的arg指定一个进程ID,负的arg表示等于arg绝对值的一个进程组ID



演示案例

  • 下面使用F_GETFL参数判断一个文件状态标志

#include<fcntl.h>
#include<stdlib.h>
int
main(int argc, char *argv[])
{
int val;
if (argc != 2)
perror("usage: a.out <descriptor#>");
if ((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0)
printf("fcntl error for fd %d", atoi(argv[1]));

//O_RDONLY、O_WRONLY、O_RDWR, O_EXEC、O_SEARCH这5中需要与O_ACCMODE配合判断
switch (val & O_ACCMODE)
{
case O_RDONLY:
printf("read only");
break;
case O_WRONLY:
printf("write only");
break;
case O_RDWR:
printf("read write");
break;
default:
err_dump("unknown access mode");
}
if (val & O_APPEND)
printf(", append");
if (val & O_NONBLOCK)
printf(", nonblocking");
if (val & O_SYNC)
printf(", synchronous writes");
#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)
if (val & O_FSYNC)
printf(", synchronous writes");
#endif

putchar(\n’);
exit(0);
}

APUE编程:17---文件I/O之(fcntl函数)_#include_03


三、F_SETFD与F_SETFL的注意事项

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


自定义函数

  • 我们对fcntl进行了封装,​其中:
  • set_fl()用于在fd上设置flags属性
  • clr_fl()取消fd上的flags属性

#include <fcntl.h>
void set_fl(int fd, int flags)
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)//先得到
perror("fcntl F_GETFL error");
val |= flags; /* turn on flags */
if (fcntl(fd, F_SETFL, val) < 0)//再设置
perror("fcntl F_SETFL error");
}
#include <fcntl.h>
void clr_fl(int fd, int flags)
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)//先得到
perror("fcntl F_GETFL error");
val &= ~ flags; /* turn off flags */
if (fcntl(fd, F_SETFL, val) < 0)//再设置
perror("fcntl F_SETFL error");
}

  • 例如:
  • set_fl(stdout,O_SYNC);   //开启同步写标志  
  • clr_fl(stdout,O_SYNC);    //关闭同步写标志  


  • 注意:​如果没有取得标志值就直接设置标志,意义为:设置该标志,取消其他所有标志
#include <fcntl.h>
void set_fl(int fd, int flags)
{
if (fcntl(fd, F_SETFL, flags) < 0)//再设置
perror("fcntl F_SETFL error");
}
//这种情况只设置flags,而取消其他所有的标志值

四、fcntl()在网络编程中的使用