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规范指定的首选方法。
c
#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函数通常用来将一个文件描述符设置为非阻塞的。
c
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函数为目标文件描述符指定宿主进程或进程组,那么被指定的宿主进程或进程组将捕获这两个信号。