文件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位)
不幸的是,三个访问方式标志(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/。