文件I/O之fcntl函数

fcntl函数可以改变已打开的文件的性质。

#include <fcntl.h>
int fcntl( int filedes, int cmd, ... /* int arg */ );
返回值:若成功则依赖于cmd,若出错则返回-1

在本节的各实例中,第三个参数总是一个整数,与上面所示函数原型中的注释部分相对应。但是在说明记录锁时,第三个参数则是指向一个结构的指针。

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)。

我们先说明这10种cmd值得前7种(后3种,在谈记录锁时说明)。我们将涉及与进程表项中各文件描述符相关联的文件描述符标志,以及每个文件表项中的文件状态标志。

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

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

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

注:应当了解很多现有的涉及文件描述符标志的程序并不使用常量FD_CLOEXEC,而是将此标志设置为0(系统默认,在exec时不关闭)或1(在exec时关闭)。

F_GETFL         对应于filedes的文件状态标志作为函数值返回。文件状态标志如下表3-3:(表3-3中的各个标志,除了三个访问方式标志(O_RDONLY、O_WRONLY以及O_RDWR)不各占一位,其他标志分别占文件状态标志的一个bit位)

index

不幸的是,三个访问方式标志(O_RDONLY、O_WRONLY以及O_RDWR)并不各占1位(由于历史原因,这三种标志的值分别是0、1和2。这三个访问方式标志用文件状态标志的后两位表示,00:O_RDONLY,01:O_WRONLY,10:O_RDWR。这三种值互斥——一个文件只能有这三种值之一)。因此首先必须用屏蔽字O_ACCMODE(0x0003)取得访问模式位(文件状态标志的后两位),然后将结果与这三种值的任一种做比较。

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

F_GETOWN    取当前接受SIGIO和SIGURG信号的进程ID或进程组ID。

F_SETOWN    设置接收SIGIO和SIGURG信号的进程ID或进程组ID。正当arg指定一个进程ID,负的arg表示等于arg绝对值的一个进程组ID。

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

程序清单3-4 对于指定的描述符打印文件标志

[root@localhost apue]# cat prog3-4.c
#include "apue.h"
#include <fcntl.h>

int 
main(int argc, char *argv[])
{
        int val;

        if(argc != 2)
                err_quit("usage: prog3-4 <descriptor#>");

        if((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0)
                err_sys("fcntl error for fd %d", atoi(argv[1]));

        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 defined(O_SYNC)
        if(val & O_SYNC)
                printf(", synchronous writes");
#endif
#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC)
        if(val & O_FSYNC)
                printf(", synchronous writes");
#endif
        putchar('\n');
        exit(0);
}

注意,我们使用了功能测试宏_POSIX_C_SOURCE,并且条件编译了POSIX.1中没有定义的文件访问标志。

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

程序清单3-5 对一个文件描述符打开一个或多个文件状态标志

[root@localhost apue]# cat prog3-5.c
#include "apue.h"
#include <fcntl.h>

void 
set_fl(int fd, int flags) /* flags are file status flags to turn to */
{
        int val;

        if((val = fcntl(fd, F_GETFL, 0)) < 0)
                err_sys("fcntl F_GETFL error");

        val |= flags;   /* turn on flags */

        if(fcntl(fd, F_SETFL, val) < 0)
                err_sys("fcntl F_SETFL error");
}

如果将中间的一条语句改为:

val &= ~flags;    /* turn flags off */

就构成另一个函数,我们称其为clr_fl。

比较fsync和fdatasync与O_SYNC标志,fsync和fdatasync在我们需要时更新文件内容,O_SYNC标志则在我们每次写至文件时更新文件内容。

fcntl的必要性:我们的程序在一个描述符(标准输出)上进行操作,但是根本不知道由shell打开的相应的文件名。因为这是shell打开的,于是不能在打开时,按我们的要求设置O_SYNC标志。fcntl则允许仅知道打开文件描述符时可以修改其性质。

 

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

posted @ 2014-01-01 08:24  ITtecman  阅读(1185)  评论(0编辑  收藏  举报