使用场景和相关用法
fcntl函数
一、fcntl 函数基础
1 |
|
-
返回值:成功时返回与
cmd相关的值(如标志、文件描述符),失败返回-1并设置errno。 -
核心作用:通过
cmd命令对fd(文件描述符)执行控制操作,涵盖文件状态、I/O 模式、锁机制等。
二、与 O_NONBLOCK 相关的操作(文件状态标志)
O_NONBLOCK 是 非阻塞模式标志,用于设置文件描述符的 I/O 行为:
-
阻塞模式(默认):当执行
read/write等操作时,若暂时无法完成(如无数据可读、缓冲区满),进程会进入睡眠等待。 -
非阻塞模式(
O_NONBLOCK):操作无法立即完成时,函数会立即返回-1并设置errno为EAGAIN或EWOULDBLOCK,不阻塞进程。
相关 cmd 命令:
-
F_GETFL:获取当前文件状态标志(包含是否设置O_NONBLOCK)1
2
3
4int flags = fcntl(fd, F_GETFL, 0); // 0 表示无额外参数
if (flags == -1) {
perror("fcntl F_GETFL failed");
} -
F_SETFL:设置文件状态标志(用于开启 / 关闭O_NONBLOCK)1
2
3
4
5
6
7
8
9// 开启非阻塞模式(保留原有标志,添加 O_NONBLOCK)
int new_flags = flags | O_NONBLOCK;
if (fcntl(fd, F_SETFL, new_flags) == -1) {
perror("fcntl F_SETFL failed");
}
// 关闭非阻塞模式(清除 O_NONBLOCK 标志)
new_flags = flags & ~O_NONBLOCK;
fcntl(fd, F_SETFL, new_flags);
适用场景:
-
网络套接字(
socket):避免recv/send阻塞导致进程卡死。 -
管道(
pipe)、FIFO:处理读写端不同步的情况。 -
设备文件(如终端、串口):实时响应 I/O 状态变化。
三、文件锁操作(进程间同步)
fcntl 支持对文件的部分或全部内容加锁,避免多进程竞争。相关 cmd 命令需配合 struct flock 结构体使用:
1 | struct flock { |
-
F_GETLK:检查锁冲突(获取当前阻塞当前锁请求的锁信息)1
2
3
4
5
6
7
8
9
10
11
12struct flock lock;
lock.l_type = F_WRLCK; // 尝试获取写锁
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; // 锁整个文件
if (fcntl(fd, F_GETLK, &lock) == -1) {
perror("fcntl F_GETLK failed");
}
if (lock.l_type != F_UNLCK) {
// 存在冲突的锁,lock.l_pid 为持有锁的进程
} -
F_SETLK:设置或释放锁(非阻塞模式,失败立即返回)1
2
3
4
5
6
7
8
9
10
11// 加写锁
lock.l_type = F_WRLCK;
if (fcntl(fd, F_SETLK, &lock) == -1) {
if (errno == EAGAIN || errno == EACCES) {
// 锁被占用,无法获取
}
}
// 解锁
lock.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &lock); -
F_SETLKW:设置或释放锁(阻塞模式,若无法获取则等待)1
2
3
4// 等待获取锁,直到成功或被信号中断
if (fcntl(fd, F_SETLKW, &lock) == -1) {
perror("fcntl F_SETLKW failed");
}
四、文件描述符管理
-
F_DUPFD:复制文件描述符(类似dup)1
2// 生成一个 >= min_fd 的新描述符,与原 fd 指向同一文件
int new_fd = fcntl(old_fd, F_DUPFD, min_fd); -
F_DUPFD_CLOEXEC:复制描述符并设置FD_CLOEXEC标志1
2// 新描述符在进程执行 exec 时自动关闭,避免资源泄漏
int new_fd = fcntl(old_fd, F_DUPFD_CLOEXEC, min_fd); -
F_GETFD/F_SETFD:获取 / 设置文件描述符标志(仅FD_CLOEXEC有效)1
2
3
4// 获取标志
int fd_flags = fcntl(fd, F_GETFD);
// 设置 FD_CLOEXEC(exec 时关闭)
fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC);
五、异步 I/O 与信号通知
-
F_SETOWN:设置接收SIGIO和SIGURG信号的进程 / 进程组1
2// 指定进程 ID 接收信号(负数表示进程组 ID)
fcntl(fd, F_SETOWN, pid); -
F_GETOWN:获取当前接收信号的进程 / 进程组 ID1
pid_t owner = fcntl(fd, F_GETOWN);
-
F_SETFL配合O_ASYNC:开启异步 I/O 通知(数据可读 / 可写时触发SIGIO)1
2int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_ASYNC | O_NONBLOCK); // 同时开启异步和非阻塞
六、其他系统相关命令
-
F_GETPIPE_SZ/F_SETPIPE_SZ:获取 / 设置管道(pipe)的缓冲区大小1
2int pipe_size = fcntl(fd, F_GETPIPE_SZ); // 获取大小
fcntl(fd, F_SETPIPE_SZ, new_size); // 设置大小(需权限) -
F_ADD_SEALS/F_GET_SEALS:为文件添加 / 查询 “密封” 标志(限制修改)1
2
3
4// 添加写密封(禁止后续写入)
fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE);
// 查询当前密封标志
int seals = fcntl(fd, F_GET_SEALS); -
F_GETLEASE/F_SETLEASE:获取 / 设置文件租约(协调进程间文件访问)1
2// 设置读租约(其他进程写操作会触发通知)
fcntl(fd, F_SETLEASE, F_RDLEASE);
fcntl 是一个功能强大的多用途函数,核心能力包括:
-
控制 I/O 模式(如
O_NONBLOCK非阻塞); -
进程间文件锁(读锁、写锁);
-
文件描述符管理(复制、标志设置);
-
异步 I/O 信号通知;
-
系统特定扩展功能(管道大小、文件密封等)。
使用时需注意:不同 Unix/Linux 系统对命令的支持可能有差异,建议通过 man 2 fcntl 查看本地系统的详细文档。非阻塞模式(O_NONBLOCK)常与 I/O 多路复用(select/poll/epoll)结合,是高性能网络编程的基础。