一、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),详细使用见文章:javascript:void(0)
F_DUPFD

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

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

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

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

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

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

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函数)_描述符_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()在网络编程中的使用