#includeint fcntl(int fd, int cmd, ... /* int arg */); /* 返回值:若成功,则依赖于 cmd;否则,返回 -1 */
在本节的各实例中,第三个参数总是一个整数,但在后面说明记录锁时,它则是一个指向一个结构的指针。
fcntl 函数有以下 5 种功能:
(1) 复制一个已有的描述符(cmd = F_DUPFD / F_DUPFD_CLOEXEC)。
(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)。
在此先说明11种 cmd 中的前8种,后3种待后边介绍记录锁时再进行介绍。我们将讨论与进程表项中各文件描述符相关联的文件描述符标志以及每个文件表项中的文件状态标志。
F_DUPFD:复制文件描述符 fd。新文件描述符作为函数值返回,它是尚未打开的各描述符中大于或等于第三个参数值(取为整型值)中各值的最小值,它与 fd 共享同一文件表项。但是,它有它自己的一套文件描述符标志,其 FD_CLOEXEC 文件描述符标志被清除(这表示该描述符在 exec 时仍保持有效。很多现有的与文件描述符标志有关的程序并不使用常量 FD_CLOEXEC,而是将此标志设置为 0 (系统默认,在 exec 时不关闭)或 1 (在 exec 时关闭))。
F_DUPFD_CLOEXEC:复制文件描述符,设置与新描述符关联的 FD_CLOEXEC 文件描述符标志的值,返回新文件描述符。
F_GETFD:对应于 fd 的文件描述符标志作为函数值返回。当前只定义了一个文件描述符标志 FD_CLOEXEC。
F_SETFD:对应于 fd 设置文件描述符标志。新标志值按第3个参数(取为整数值)设置。
F_GETFL:对应于 fd 的文件状态标志作为函数值返回。遗憾的是,open 函数中的 5 个文件状态标志(O_RDONLY、O_WRONLY、O_RDWR、O_EXEC 以及 O_SEARCH)并不各占 1 位,而且这 5 个值互斥,一个文件的访问方式只能取其中之一。因此首先必须用屏蔽字 O_ACCMODE 取得访问方式位,然后将结果与这 5 个值中的每一个相比较。
F_SETFL:将文件状态标志设置为第 3 个参数的值(取为整数值)。可以更改的几个标志是: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。
下面这个示例可用来打印指定文件描述符所选择的文件状态标志。
#include#include #include int main(int argc, char *argv[]){ int val; if(argc != 2){ printf("usage: %s \n", argv[0]); exit(1); } if((val=fcntl(atoi(argv[1]), F_GETFL, 0)) < 0){ printf("fcntl() error for fd %s\n", argv[1]); exit(2); } 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: printf("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); }
下面是从 bash 中执行的结果(使用其它 shell 可能结果会有所不同):
$./a.out 0 < /dev/tty read only $./a.out 1 > temp.foo $cat temp.foo write only $./a.out 2 2>>temp.foo write only, append $./a.out 5 5<>temp.foo # 表示在文件描述符 5 上打开文件 temp.foo 以供读、写 read write
在修改文件描述符标志或文件状态标志时必须谨慎,要先获得现在的标志值,然后按照期望修改它,最后设置新标志值,不能只是执行 F_SETFD 或 F_SETFL 命令,这样会关闭以前设置的标志位。下面是对一个文件描述符设置一个或多个文件状态标志的函数示例:
#include#include void set_fl(int fd, int flags){ // flags are file status flags to turn on. int val; if((val=fcntl(fd, F_GETFL, 0)) < 0){ printf("fcntl F_GETFL error\n"); exit(2); } val |= flags; // turn on flags if(fcntl(fd, F_SETFL, val) < 0){ printf("fcntl F_SETFL error\n"); exit(2); } } void clr_fl(int fd, int flags){ // flags are file status flags to turn off. int val; if((val=fcntl(fd, F_GETFL, 0)) < 0){ printf("fcntl F_GETFL error\n"); exit(2); } val &= ~flags; // turn flags off if(fcntl(fd, F_SETFL, val) < 0){ printf("fcntl F_SETFL error\n"); exit(2); } }