目录
4. F_GETLK / F_SETLK / F_SETLKW
前言
fcntl
是 Unix 和 Linux 系统编程中的一个系统调用,用于操作文件描述符。fcntl
提供了一种通用的接口,可以用来控制文件描述符的属性和行为,支持文件加锁、文件状态标志的修改、文件描述符复制等功能。
一、fcntl
的函数原型
int fcntl(int fd, int cmd, ... /* arg */ );
fd
:要操作的文件描述符。cmd
:要执行的控制命令。arg
:根据cmd
的不同,可能需要传递的附加参数(可选)。
二、常用的 fcntl
命令
cmd
参数指定了要执行的操作,常用的命令包括以下几种:
1. F_DUPFD / F_DUPFD_CLOEXEC
-
功能:复制文件描述符。
F_DUPFD
:从给定的最小值开始寻找空闲的文件描述符,并返回新的文件描述符。F_DUPFD_CLOEXEC
:同样是复制文件描述符,但设置FD_CLOEXEC
标志(该标志会使得在执行exec
系列函数时自动关闭新描述符)。
-
使用场景:可以用于复制文件描述符,从而使多个不同的文件描述符指向同一个文件。常用于文件共享、重定向等操作。
-
示例:
int new_fd = fcntl(fd, F_DUPFD, 0);
2. F_GETFD / F_SETFD
-
功能:获取或设置文件描述符的标志(
FD_CLOEXEC
)。F_GETFD
:获取文件描述符标志。F_SETFD
:设置文件描述符标志,常用于设置FD_CLOEXEC
。
-
使用场景:在进程间继承文件描述符时,设置
FD_CLOEXEC
标志可以确保文件描述符不会在exec
系列函数执行时被继承。 -
示例:
int flags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, flags | FD_CLOEXEC); // 设置 FD_CLOEXEC 标志
3. F_GETFL / F_SETFL
-
功能:获取或设置文件描述符的状态标志(文件状态标志)。
F_GETFL
:获取文件状态标志。F_SETFL
:设置文件状态标志,例如设置成非阻塞模式、同步写等。
-
常见的文件状态标志:
O_NONBLOCK
:设置文件描述符为非阻塞模式。O_APPEND
:追加模式写入。O_SYNC
:同步模式写入。
-
使用场景:可以用于将文件描述符设为非阻塞模式,或者设置文件的写入同步性等。
-
示例:
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK); // 设置为非阻塞模式
4. F_GETLK / F_SETLK / F_SETLKW
-
功能:获取或设置文件锁。
F_GETLK
:查询文件锁状态。F_SETLK
:设置文件锁,非阻塞模式。F_SETLKW
:设置文件锁,阻塞模式。
-
文件锁类型:
F_RDLCK
:共享读锁,多个进程可以同时持有。F_WRLCK
:独占写锁,只有一个进程可以持有写锁。F_UNLCK
:解锁。
-
使用场景:进程间的文件访问控制,防止多个进程同时读写同一个文件。
-
示例:
struct flock lock;
lock.l_type = F_WRLCK; // 申请写锁
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; // 锁定整个文件
fcntl(fd, F_SETLK, &lock); // 非阻塞申请写锁
5. F_SETOWN / F_GETOWN
-
功能:设置或获取接收信号的进程或进程组 ID。
F_SETOWN
:设置接收SIGIO
信号的进程或进程组。F_GETOWN
:获取当前接收SIGIO
信号的进程或进程组。
-
使用场景:主要用于异步 I/O 操作,当文件描述符可读或可写时,内核会发送
SIGIO
信号给设定的进程或进程组。 -
示例:
fcntl(fd, F_SETOWN, getpid()); // 设置当前进程为 SIGIO 信号的接收者
三、fcntl
的常见应用场景
-
文件描述符的复制:
使用F_DUPFD
复制文件描述符。常见于 I/O 重定向场景,如重定向标准输入、标准输出等。 -
文件锁机制:
使用F_SETLK
、F_SETLKW
等进行文件加锁,以防止多个进程或线程同时读写同一个文件。尤其适合并发编程中的文件访问控制。 -
非阻塞 I/O:
使用F_GETFL
和F_SETFL
结合O_NONBLOCK
标志将文件描述符设为非阻塞模式,常用于网络编程中的非阻塞 socket 连接。 -
控制
使用FD_CLOEXEC
标志:F_GETFD
和F_SETFD
设置FD_CLOEXEC
,确保文件描述符在执行exec()
系列函数时不会被继承。