可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File StatusFlag),而不必重新open 文件。 #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);
这个函数和open 一样,也是用可变参数实现的,可变参数的类型和个数取决于前面的cmd 参数。
针对第2个参数,int cmd fcntl函数有五种功能: • 复制一个现存的描述符(cmd=F_DUPFD) 。 • 获得/设置文件描述符标记(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 复制文件描述符filedes,新文件描述符作为函数值返回。它是尚未打开的各 描述符中大于或等于第三个参数值(取为整型值)中各值的最小值。新描述符与 filedes 共享同 一文件表项。但是,新描述符有它自己的一套文件描述符标志,其 F D _ C L O E X E C 文件描述符标志则被清除。 • F_GETFD 对应于filedes 的文件描述符标志作为函数值返回。当前只定义了一个文件描 述符标志FD_CLOEXEC。 • F_SETFD 对于filedes 设置文件描述符标志。新标志值按第三个参数 (取为整型值)设置。 应当了解很多现存的涉及文件描述符标志的程序并不使用常数 F D _ C L O E X E C,而是将此 标志设置为0(系统默认,在exec时不关闭)或1(在exec时关闭)。
二~文件描述符号,套接口 属性相关 • F_GETFL 对应于filedes 的文件状态标志作为函数值返回。在说明 open函数时,已说明 了文件状态标志 不幸的是,三个存取方式标志 (O_RDONLY,O_WRONLY,以及O_RDWR)并不各占1位。(正 如前述,这三种标志的值各是 0、1和2,由于历史原因。这三种值互斥 — 一个文件只能有这 三种值之一。 )因此首先必须用屏蔽字 O_ACCMODE相与 取得存取方式位,然后将结果与这三种值 相比较。 • F_SETFL 将文件状态标志设置为第三个参数的值 (取为整型值)。 可以更改的几个标志是: O_APPEND,O_NONBLOCK,O_SYNC和O_ASYNC。
fcntl的文件状态标志共有7个,O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_NONBLOCK,O_SYNC和O_ASYNC
三~信号驱动I/O , 带外数据,设置套接口接受信号的属主 SIGIO,跟信号驱动I/O有关 SIGURG, 和接受带外数据有关 • F_GETOWN 取当前接收SIGIO和SIGURG信号的进程ID或进程组ID。12.6.2节将论述这 两种4.3+BSD异步I/O信号。 • F_SETOWN 设置接收SIGIO和SIGURG信号的进程ID或进程组ID。正的arg指定一个进 程ID,负的arg表示等于arg绝对值的一个进程组ID。
下面的例子使用F_GETFL和F_SETFL这两种fcntl 命令改变STDIN_FILENO的属性,上O_NONBLOCK 选项,实现非阻塞读终端的功能。
用fcntl改变File Status Flag #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <stdlib.h> #define MSG_TRY "try again\n" int main(void) { char buf[10]; int n; int flags; flags = fcntl(STDIN_FILENO, F_GETFL); flags |= O_NONBLOCK; if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) { perror("fcntl"); exit(1); } tryagain: n = read(STDIN_FILENO, buf, 10); if (n < 0) { if (errno == EAGAIN) { sleep(1); write(STDOUT_FILENO, MSG_TRY,strlen(MSG_TRY)); goto tryagain; } perror("read stdin"); exit(1); } write(STDOUT_FILENO, buf, n); return 0; }
|