Linux提供了很多高级的I/O函数。它们并不像Linux基础I/O函数(比如open和read)那么常用(编写内核模块时一般要实现这些I/O函数),但在特定的条件下却表现出优秀的性能。这些函数大致分为三类:
用于创建文件描述符的函数,包括pipe、socketpair、dup/dup2函数。
用于读写数据的函数,包括readv/writev、sendfile、mmap/munmap、splice和tee函数。
用于控制I/O行为和属性的函数,包括fcntl函数。
本节接着介绍第三类
一、fcntl函数
fcntl
函数全称是 file control
。该函数提供了对文件描述符的各种控制操作。另外一个常见的控制文件描述符属性和行为的系统调用是ioctl
,而且ioctl
比fcntl
能够执行更多的控制。但是,对于控制文件描述符常用的属性和行为,fcntl
函数是由POSIX规范指定的首选方法。
#include <fcntl.h>int fcntl(int fd, int cmd, ...);
-
fd
参数是被操作的文件描述符 -
cmd
参数指定执行何种类型的操作
下面是一些常见的操作
cmd | 含义 | 第三个参数类型 | 成功时返回值 |
---|---|---|---|
F_DUPFD | 复制文件描述符 | int | 复制的新文件描述符 |
F_GETFD | 获取文件描述符标志 | 无 | 文件描述符标志位(整数) |
F_SETFD | 设置文件描述符标志 | int | 0(成功)或 -1(失败) |
F_GETFL | 获取文件状态标志 | 无 | 文件状态标志位(整数) |
F_SETFL | 设置文件状态标志 | int | 0(成功)或 -1(失败) |
F_GETLK | 获取记录锁 | struct flock * | 0(无冲突)或 -1(失败) |
F_SETLK | 设置记录锁 | const struct flock * | 0(成功)或 -1(失败) |
F_SETLKW | 设置记录锁(阻塞版本) | const struct flock * | 0(成功)或 -1(失败) |
F_GETOWN | 获取异步I/O所有权 | 无 | 所有权的进程ID(整数) |
F_SETOWN | 设置异步I/O所有权 | pid_t | 0(成功)或 -1(失败) |
F_GETLK64 | 获取记录锁(64位版本) | struct flock64 * | 0(无冲突)或 -1(失败) |
F_SETLK64 | 设置记录锁(64位版本) | const struct flock64 * | 0(成功)或 -1(失败) |
F_SETLKW64 | 设置记录锁(阻塞版本,64位版本) | const struct flock64 * | 0(成功)或 -1(失败) |
F_GETSIG | 获取信号 | 无 | 信号值(整数) |
F_SETSIG | 设置信号 | int | 0(成功)或 -1(失败) |
F_GETLEASE | 获取文件租约状态 | 无 | 租约状态(整数) |
F_SETLEASE | 设置文件租约 | int | 0(成功)或 -1(失败) |
F_NOTIFY | 监视文件状态变化 | int | 0(成功)或 -1(失败) |
F_GETPIPE_SZ | 获取管道容量 | 无 | 管道容量(整数) |
F_SETPIPE_SZ | 设置管道容量 | int | 0(成功)或 -1(失败) |
F_GET_SEALS | 获取文件封印状态 | 无 | 文件封印状态(整数) |
F_ADD_SEALS | 添加文件封印 | unsigned int | 0(成功)或 -1(失败) |
F_GET_RW_HINT | 获取读写优化提示 | 无 | 读写优化提示(整数) |
F_SET_RW_HINT | 设置读写优化提示 | unsigned int | 0(成功)或 -1(失败) |
F_GET_FILE_RW_HINT | 获取文件读写优化提示 | 无 | 读写优化提示(整数) |
F_SET_FILE_RW_HINT | 设置文件读写优化提示 | unsigned int | 0(成功)或 -1(失败) |
F_GETLKPID | 获取记录锁并返回占有者的进程ID | struct flock * | 占有者的进程ID(整数)或 -1(失败) |
F_OFD_GETLK | 获取OFD记录锁 | struct flock * | 0(无冲突)或 -1(失败) |
F_OFD_SETLK | 设置OFD记录锁 | const struct flock * | 0(成功)或 -1(失败) |
F_OFD_SETLKW | 设置OFD记录锁(阻塞版本) | const struct flock * | 0(成功)或 -1(失败) |
在常见的编程中,fcntl函数通常用来将一个文件描述符设置为非阻塞的。
int setnonblocking(int fd) {/* 获取文件描述符旧的状态标志 */int old_option=fcntl(fd,F_GETFL);/* 设置非阻塞标志 */int new_option=old_option|O_NONBLOCK;/* 写入设置 */fcntl(fd,F_SETFL,new_option);/* 返回文件描述符旧的状态标志,以便日后恢复该状态标志 */return old_option;
}
此外,SIGIO
和SIGURG
这两个信号与其他Linux信号不同,它们必须与某个文件描述符相关联方可使用:当被关联的文件描述符可读或可写时,系统将触发SIGIO
信号;当被关联的文件描述符(而且必须是一个socket)上有带外数据可读时,系统将触发SIGURG
信号。将信号和文件描述符关联的方法,就是使用fcntl函数为目标文件描述符指定宿主进程或进程组,那么被指定的宿主进程或进程组将捕获这两个信号。