【Linux 编程】:深入解析 fcntl 函数
📃个人主页:island1314
🔥个人专栏:Linux—登神长阶
⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞
- 生活总是不会一帆风顺,前进的道路也不会永远一马平川,如何面对挫折影响人生走向 – 《人民日报》
🔥 目录
- 一、序言
- 二、认识 fcntl 函数
- 1. 函数介绍
- 2. 函数用途
- 3. 错误处理
- 4. 示例代码
- 三、复制文件描述符
- 1. F_DUPFD
- 2. F_DUPFD_CLOEXEC
- 3. 二者区别
- 4. 补充 -- FD_CLOEXEC 意义
- 四、获取/设置文件描述符标志
- 1. F_GETFD
- 2. F_SETFD
- 五、获取/设置文件状态标志
- 1. 文件状态标志(File Status Flags)
- 2. fcntl 命令详解
- 1. F_GETFL: 获取文件状态标志
- 2. F_SETFL: 设置文件状态标志
- 3. 文件描述符标志 vs 文件状态标志
- 六、获取/设置记录锁
- 1. 记录锁的基本概念
- 2. struct flock 结构体
- 2. fcntl 命令详解
- 2.1 F_GETLK: 检测锁冲突
- 2.2 F_SETTLK: 尝试加锁/解锁
- 2.3 F_SETLKW: 阻塞等待加锁
一、序言
🔥fcntl
函数是一个在 UNIX 和类 UNIX 系统(如 Linux)上用来操作文件描述符的系统调用
- 作用:可以用于改变文件描述符的属性或状态,或者执行基本的控制操作
- 场景:fcntl 函数非常强大且灵活,常用于实现各种文件和进程间通信的功能
二、认识 fcntl 函数
1. 函数介绍
函数原型
#include
#include
int fcntl(int fd, int cmd, ... /* arg */ );
// arg表示可变参数,由cmd决定
参数说明
fd
:文件描述符,指定要操作的文件或套接字的描述符cmd
:控制命令,指示要执行的操作类型fcntl()
的第三个参数是可选,是否需要此参数由cmd
决定- 所需的参数类型在每个
cmd
名称后面的括号中指示(在大多数情况下,所需的类型是int,我们使用名称arg来标识参数),如果不需要参数,则指定void
- 例如:对于
F_SETFL
命令,可以传递新的状态标志
- 所需的参数类型在每个
返回值
- 成功时返回命令的结果,通常是状态标志或锁信息
- 失败时返回 -1,并设置
errno
以指示错误类型
常见的命令包括:
-
复制一个现有的描述符
F_DUPFD
:复制文件描述符F_DUPFD_CLOEXEC
:复制文件描述符,设置FD_CLOEXEC标志
-
获得/设置文件状态标记
F_GETFL
:获取文件描述符的当前状态标志F_SETFL
:设置文件描述符的状态标志
-
获得/设置文件描述符标记
F_GETFD
:获取文件描述符的内部标志
F_SETFD
:设置文件描述符的内部标志
-
获得/设置异步I/O所有权
F_GETOWN
:获得异步I/O所有权F_SETOWN
:设置异步I/O所有权
-
获得/设置记录锁
F_GETLK
:获取文件锁定信息
F_SETLK
:设置文件锁定信息
F_SETLKW
:以阻塞方式设置文件锁定信息。
注意:以下某些操作仅在特定的
Linux
内核版本之后才受支持。检查主机内核是否支持特定操作的首选方法是使用所需的cmd
值调用fcntl()
,然后使用EINVAL测试调用是否失败,这表明内核是否能够识别该值
2. 函数用途
1️⃣获取和设置文件状态标志
作用:设置文件为非阻塞模式
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK); // 将这个描述符设置为非阻塞模式
2️⃣文件锁定
作用:防止多个进程同时写入同一文件
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); // 设置锁定
3️⃣更改文件描述符的属性
作用:动态更改文件描述符的功能,例如 将其设置为 异步I/O工作模式 或 实时信号
3. 错误处理
使用 fcntl
函数时,如果返回值为 -1,可以通过 errno 获取具体错误信息。常见的错误如下:
EBADF
:提供的文件描述符无效EINTR
:操作被信号中断EINVAL
:指定的命令无效或者参数不符合规范
4. 示例代码
#include
#include
#include
int main() {
int fd = open("example.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open");
return 1;
}
// 获取当前的文件状态标志
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
perror("fcntl get");
return 1;
}
// 设置为非阻塞模式
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl set");
return 1;
}
// 关闭文件描述符
close(fd);
return 0;
}
三、复制文件描述符
1. F_DUPFD
int fcntl(int fd, F_DUPFD, int n);
/* 参数说明 */
fd: 要复制的原始文件描述符。
n: 分配的新文件描述符的起始值。如果可用,新的文件描述符将返回一个大于或等于 n 的最小的文件描述符。
功能:
- 创建一个新的文件描述符,该描述符是现有文件描述符的副本
- 新的文件描述符将从指定的最小文件描述符值开始分配,使用大于或等于arg参数的编号最低的可用文件描述符
- 新描述符与旧 fd 共享同一文件表项
- 但是:新描述符有它自己的一套文件描述符标志,其
FD_CLOEXEC
文件描述符标志被清除〈这表示该描述符在 exec 时仍保持打开状态)
#include
#include
#include
#include
int main()
{
int fd = open("./fcntl_F_DUPFD.txt", O_RDWR | O_CREAT | O_TRUNC, 0775);
int fcntlFd = fcntl(fd, F_DUPFD, 0); // 指定从 0 开始分配最小的可用描述符作为新描述符
int dupFd = dup(fd); // 等效于 fcntl(fd, F_DUPFD, 0);
close(fd);
close(fcntlFd);
close(dupFd);
return 0;
}
2. F_DUPFD_CLOEXEC
int fcntl(int fd, F_DUPFD_CLOEXEC, int n);
功能:
- 类似于
F_DUPFD
,区别在于F_DUPFD_CLOEXEC
在复制的同时会设置文件描述符标志FD_CLOEXEC
- 这意味着如果当前进程调用 exec 系列函数时,将自动关闭这个文件描述符
3. 二者区别
-
dup
和F_DUPFD
:新描述符的FD_CLOEXEC
默认为0
,无论原描述符是否设置了该标志(都不会保留FD_CLOEXEC
标志) -
F_DUPFD_CLOEXEC
:新描述符的FD_CLOEXEC
强制为1
,覆盖原描述符的设置 -
F_GETFD
:仅读取文件描述符标志(如FD_CLOEXEC
),不会修改标志状态int flags = fcntl(fd, F_GETFD); // 获取标志 if (flags & FD_CLOEXEC) { /* 检查 FD_CLOEXEC 是否设置 */ }
示例代码
样例一
#include
#include
#include
int main() {
int fd = open("testfile", O_RDWR | O_CREAT, 0644);
if (fd == -1) { perror("open"); return 1; }
// 设置原描述符的 FD_CLOEXEC
fcntl(fd, F_SETFD, FD_CLOEXEC);
// 测试 dup
int dup_fd = dup(fd);
int dup_flags = fcntl(dup_fd, F_GETFD);
printf("dup_fd FD_CLOEXEC: %d
", (dup_flags & FD_CLOEXEC) ? 1 : 0); // 输出 0
// 测试 F_DUPFD_CLOEXEC
int cloexec_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
int cloexec_flags = fcntl(cloexec_fd, F_GETFD);
printf("cloexec_fd FD_CLOEXEC: %d
", (cloexec_flags & FD_CLOEXEC) ? 1 : 0); // 输出 1
close(fd);
close(dup_fd);
close(cloexec_fd);
return 0;
}
样例二
#include
#include
#include
#include
int main()
{
//fd 设置标记 fcntlFd 不设置 fcntlCloFd 设置 dupFd 不设置
int fd = open("./fcntl_F_DUPFD_CLOEXEC", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0775);
int fcntlFd = fcntl(fd, F_DUPFD, 0);
int fcntlCloFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
int dupFd = dup(fd);
//通过fcntl(fd, F_GETFD, 0);获取状态标记
int fdFlag = fcntl(fd, F_GETFD, 0);
int fcntlFdFlag = fcntl(fcntlFd, F_GETFD, 0);
int fcntlCloFdFlag = fcntl(fcntlCloFd, F_GETFD, 0);
int dupFdFlag = fcntl(dupFd, F_GETFD, 0);
// 结果是:fdFlag=1, fcntlFdFlag=0, fcntlCloFdFlag=1, dupFdFlag=0
printf("fdFlag=%d, fcntlFdFlag=%d, fcntlCloFdFlag=%d, dupFdFlag=%d
",
fdFlag,fcntlFdFlag,fcntlCloFdFlag,dupFdFlag);
close(fd);
close(fcntlFd);
close(fcntlCloFd);
close(dupFd);
return 0;
}
4. 补充 – FD_CLOEXEC 意义
♐️ 文件描述符标志FD_CLOEXEC
,用来表示该描述符在执行完 fork+exec
系列函数创建子进程时会自动关闭,以防止它们被传递给子进程。那么为什么要这样做呢?
原因:因为当一个进程调用
exec
系列函数(比如execve
)来创建子进程时,所有打开的文件描述符都会被传递给子进程
- 如果文件描述符没有设置
FD_CLOEXEC
标志,这些文件将保持打开状态并继续对子进程可见- 这样就可能导致潜在的安全风险或者意外行为
四、获取/设置文件描述符标志
🏖 fcntl
函数中的 F_GETFD
和 F_SETFD
命令用于获取和设置文件描述符的标志,这在操作文件描述符的行为时非常重要
1. F_GETFD
int fcntl(int fd, F_GETFD);
// 参数说明:
fd: 要查询状态的文件描述符
功能:获取指定文件描述符的标志
返回值:返回文件描述符的标志,如果失败则返回 -1,并设置 errno。主要的标志如下:
FD_CLOEXEC
:文件描述符在 exec 系列调用时会被关闭。如果该标志被设置,返回值包含该标志。- 如果返回值与
FD_CLOEXEC
持平,表示标志已设置;如果返回值包含 0,表示没有设置
2. F_SETFD
int fcntl(int fd, F_SETFD, int flags);
// 参数说明:
fd: 要设置状态的文件描述符。
flags: 新的状态标志,可以是以下值之一,或者是它们的组合:
FD_CLOEXEC: 设置该标志,表示在调用 exec 系列函数时关闭文件描述符
功能:设置指定文件描述符的标志
上面我们讲了 FD_CLOEXEC
,下面来演示一下如何获取
文件描述符的 FD_CLOEXEC 标志可以通过三个方法得到:
- 调用
open
函数是,指定O_CLOEXEC
- 通过
fcntl
函数使用F_DUPFD_CLOEXEC
复制文件描述符,新的描述符就是FD_CLOEXEC
- 通过
fcntl
函数使用F_SETFD
直接设置FD_CLOEXEC
代码示例
#include
#include
#include
#include
int main()
{
int fd = open("./fcntl_F_GETFD", O_RDWR | O_CREAT | O_TRUNC, 0775);
int fdCloExec = open("./fcntl_F_GETFD2", O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0775);
int fdCloExecDup = fcntl(fd, F_DUPFD_CLOEXEC, 0);
int fdSetFd = dup(fd);
fcntl(fdSetFd, F_SETFD, FD_CLOEXEC);
int flagFd = fcntl(fd, F_GETFD);
int flagFdCloExec = fcntl(fdCloExec, F_GETFD);
int flagFdCloExecDup = fcntl(fdCloExecDup, F_GETFD);
int flagFdSetFd = fcntl(fdSetFd, F_GETFD);
// 打印结果:flagFd=0, flagFdCloExec=1, flagFdCloExecDup=1 flagFdSetFd=1
printf("flagFd=%d, flagFdCloExec=%d, flagFdCloExecDup=%d flagFdSetFd=%d
",
flagFd,flagFdCloExec,flagFdCloExecDup,flagFdSetFd);
close(fd);
close(fdCloExec);
close(fdCloExecDup);
close(fdSetFd);
return 0;
}
分析
flagFd=0
:fd
通过open(...)
创建时未设置O_CLOEXEC
,标志未启用flagFdCloExec=1
:fdCloExec
在open
时指定了O_CLOEXEC
,标志已设置flagFdCloExecDup=1
:F_DUPFD_CLOEXEC
自动设置FD_CLOEXEC
flagFdSetFd=1
:dup
后通过F_SETFD
显式设置标志
五、获取/设置文件状态标志
🛠 fcntl 函数中的 F_GETFL
和 F_SETFL
命令用于获取和设置文件的状态标志,这些标志控制文件的行为和特性。
1. 文件状态标志(File Status Flags)
- 作用 :控制文件的行为,如读写模式、追加、非阻塞、同步等。
- 常用标志 :
- 访问模式 :
O_RDONLY
(只读)、O_WRONLY
(只写)、O_RDWR
(读写)。 - 操作模式 :
O_APPEND
(追加)、O_NONBLOCK
(非阻塞)、O_SYNC
(同步写入)、O_ASYNC
(异步 I/O)。 - 兼容性标志 :
O_DIRECT
(直接 I/O)、O_NOATIME
(不更新访问时间)。
- 访问模式 :
标志值与系统差异
- 访问模式 :
O_RDONLY
:0(二进制0b00
)。O_WRONLY
:1(0b01
)。O_RDWR
:2(0b10
)。- 这些值属于位掩码的一部分,需通过
O_ACCMODE
提取。
- 其他标志 :
O_APPEND
:0x400(十六进制)。O_NONBLOCK
:0x800(Linux 中对应八进制04000
)。O_ASYNC
:0x2000(十六进制)- 一些其他标志具体值因系统而异
2. fcntl 命令详解
1. F_GETFL: 获取文件状态标志
#include
int fcntl(int fd, F_GETFL);
- 返回值 :
- 成功:返回标志值(如
O_RDWR | O_APPEND
) - 失败:返回
-1
,并设置errno
- 成功:返回标志值(如
访问方式标志:O_RDONLY
、O_WRONLY
、O_RDWR
。这3个值是互斥的,因此首先必须用屏蔽 O_ACCMODE
取得访问方式位,然后将结果与这3个值中的每一个相比较。
示例:访问模式处理
int flags = fcntl(fd, F_GETFL);
int access_mode = flags & O_ACCMODE; // 提取访问模式
if (access_mode == O_RDONLY) { /* 只读 */ }
2. F_SETFL: 设置文件状态标志
#include
int fcntl(int fd, F_SETFL, int flags);
返回值:
- 成功返回 0
- 失败返回 -1
注意:fcntl(fd, F_SETFL, flags)
并非所有标志都可通过此命令修改 。根据 Linux 手册页和 POSIX 标准,F_SETFL
可以设置的标志包括以下常见选项:
标志名 | 用途说明 | 应用场景 |
---|---|---|
O_APPEND | 强制每次写操作前将文件偏移量设置到文件末尾(追加模式)。 | 日志文件(确保多进程写入时自动追加到文件末尾) |
O_NONBLOCK | 设置非阻塞模式(对设备、管道、套接字等有意义) | 网络编程( I/O 多路复用) |
O_ASYNC | 异步 I/O 通知(当数据可读/写时发送信号,需特定系统支持) | 异步信号驱动 I/O(如通过SIGIO 信号触发处理逻辑) |
O_DIRECT | 绕过内核缓冲区(直接 I/O,减少内存拷贝,需硬件/文件系统支持) | 高性能数据库(绕过内核缓冲区,减少内存拷贝) |
O_NOATIME | 读取文件时不更新文件的访问时间(atime ),用于优化性能 | 文件系统优化(避免频繁更新atime ,减少磁盘 I/O) |
注意 :
O_TRUNC
(截断文件)和O_CREAT
(创建文件)等标志只能通过open()
设置 ,不能通过fcntl(F_SETFL)
修改O_SYNC
(同步写入,确保数据落盘)等标志在某些系统(如 Linux)中也属于F_SETFL
的可修改范围,但需注意其行为可能与O_DSYNC
等标志有差异
最常用标志:O_NONBLOCK
- 非阻塞 I/O :当读写套接字、管道或设备时,若没有数据可读或缓冲区满,操作会立即返回而非阻塞等待。
- 多路复用结合使用 :与
select()
、poll()
、epoll()
等配合,实现高性能 I/O 多路复用。 - 避免阻塞线程 :在事件驱动模型(如 Reactor 模式)中,非阻塞模式是实现高并发的基础
int flags = fcntl(sockfd, F_GETFL); // 获取当前标志
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); // 设置非阻塞模式
示例代码:设置非阻塞
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const int MAX = 1024;
void SetNonBlock(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if(flags < 0){
std::cout << "Get flags error" << std::endl;
return;
}
flags |= O_NONBLOCK; // O_NONBLOCK = 04000 :让 fd 以非阻塞的方式工作
if(fcntl(fd, F_SETFL, flags) < 0){
std::cout << "Set flags error" << std::endl;
}
}
int main()
{
std::string tips = "Please Enter# ";
char buffer[MAX];
SetNonBlock(0);
while(true)
{
write(0, tips.c_str(), tips.size());
// 非阻塞,如果我们不输入,数据就不会读取(就绪),所以会一直循环,以出错形式返回
// read 不是有读取失败(-1),失败 vs 底层数据没就绪 -> 底层数据没就绪其实不算失败
// 如果是 -1,失败 vs 底层数据没就绪 后续的做法不同的,需要区分的必要性
// errno:更详细的出错原因,最近一次调用出错的错误码
int n = read(0, buffer, sizeof(buffer));
if(n > 0){
buffer[n] = 0;
std::cout << "Read " << n << " echo# " << buffer << std::endl;
}
else if(n == 0){ // 在 标准输入中,Ctrl + d 退出
std::cout << "Read over" << std::endl;
break;
}
else{
if(errno == EAGAIN || errno == EWOULDBLOCK){ // 11(try again) || (Operation would block)
std::cout << "Data not ready" << std::endl; // 底层数据没就绪
}
else if(errno == EINTR)
{
std::cout << "Interrupted system call" << std::endl; // 被中断,重新来过
sleep(1);
continue;
}
else std::cout << "Read error: " << n << ", errno " << errno << std::endl;
}
sleep(1);
}
return 0;
}
3. 文件描述符标志 vs 文件状态标志
文件描述符标志(File Descriptor Flags)
- 通过
fcntl(F_SETFD)
修改。 - 主要控制描述符的生命周期和继承性,例如:
FD_CLOEXEC
:在execve()
时自动关闭描述符
文件状态标志(File Status Flags)
- 通过
fcntl(F_SETFL)
修改。 - 影响文件描述符(File Descriptor)的行为,例如:
O_APPEND
:写入时自动定位到文件末尾O_NONBLOCK
:非阻塞模式
六、获取/设置记录锁
⛵️ 在 Unix 和类 Unix 系统中,fcntl 函数提供了用于记录锁(record locks)的功能,通过命令 F_GETLK、F_SETLK 和 F_SETLKW 来实现。这些命令用于获取、设置和管理文件的记录锁,帮助实现进程间的同步。
1. 记录锁的基本概念
Linux实现了 POSIX
标准化的传统(“进程相关”)UNIX记录锁
- 记录锁 (
record locking
)的功能是:当一个进程正在读或修改文件的某个部分时,它可以阻止其他进程修改同一文件区。 - 对于UNIX系统而言,“记录”这个词是一种误用,因为
UNIX
系统内核根本 没有使用 文件记录 这种概念 - 更适合的术语可能是字节范围锁 (
byte-rangelocking
),因为它锁定的只是文件中的一个区域(也可能是整个文件)
- 字节范围锁(Byte-range Locking) :
- UNIX/Linux 的记录锁本质是对文件的某个字节范围加锁 ,而非传统意义上的“记录”。
- 例如:锁定文件从偏移
100
到200
字节的区域,其他进程不能修改该区域。
- 锁的类型 :
- 读锁(
F_RDLCK
) :共享锁,允许多个进程同时读取锁定区域,但阻止写操作。 - 写锁(
F_WRLCK
) :独占锁,阻止其他进程读写锁定区域。 - 解锁(
F_UNLCK
) :释放已持有的锁。
- 读锁(
- 锁的继承与释放 :
- 进程终止时,所有锁自动释放。
fork()
子进程不会继承父进程的锁。execve()
后,若文件描述符设置了FD_CLOEXEC
,锁会被释放。
F_SETLK
、F_SETLKW
和F_GETLK
用于获取、释放和测试记录锁(也称为字节范围、文件段或文件区域锁)的存在。使用记录锁时,第三个参数是指向 struct flock 结构的指针
2. struct flock 结构体
strucy flock 结构体定义如下:
struct flock {
short l_type; // 锁类型: F_RDLCK, F_WRLCK, F_UNLCK
short l_whence; // 偏移起点: SEEK_SET, SEEK_CUR, SEEK_END
off_t l_start; // 偏移量
off_t l_len; // 锁定区域长度 (0 表示到文件末尾)
pid_t l_pid; // 持有锁的进程 ID (仅用于 F_GETLK)
};
l_whence
、l_start
、l_len
这三个参数用于分段对文件加锁,若对整个文件加锁,则:l_whence
= SEEK_SET,l_start
= 0,l_len
= 0l_type
有三种状态 :F_RDLCK
建立一个供读取用的锁定,允许其他进程读该文件,但不允许其他进程写该文件F_WRLCK
建立一个供写入用的锁定,不允许其他进程读、写该文件F_UNLCK
删除之前建立的锁定
l_whence
也有三种方式:SEEK_SET
以文件开头为锁定的起始位置SEEK_CUR
以目前文件读写位置为锁定的起始位置SEEK_END
以文件结尾为锁定的起始位置。
2. fcntl 命令详解
2.1 F_GETLK: 检测锁冲突
-
功能 :测试是否可以对指定区域加锁,不会实际加锁
-
行为 :
- 如果存在冲突锁,
fcntl
返回0
,并将冲突锁的信息填充到flock
结构体中(如l_type
为冲突锁类型,l_pid
为持有锁的进程 ID)。 - 如果没有冲突锁,
l_type
会被设置为F_UNLCK
。
- 如果存在冲突锁,
-
错误处理 :此命令不会设置
errno
,返回值始终为0
-
示例:
struct flock lock = {0}; lock.l_type = F_WRLCK; // 测试写锁 lock.l_whence = SEEK_SET; lock.l_start = 0; // 从文件开头 lock.l_len = 1024; // 锁定前 1KB if (fcntl(fd, F_GETLK, &lock) == 0) { if (lock.l_type == F_UNLCK) { printf("No conflicting lock. "); } else { printf("Conflicting lock by PID %d ", lock.l_pid); } }
2.2 F_SETTLK: 尝试加锁/解锁
-
功能 :尝试对指定区域加锁或解锁。
-
行为 :
- 加锁 :若无冲突,立即加锁;若有冲突,返回
-1
,errno
设置为EACCES
或EAGAIN
- 解锁 :直接释放指定区域的锁
- 加锁 :若无冲突,立即加锁;若有冲突,返回
-
错误处理 :
- 冲突锁存在时返回
-1
,errno = EACCES/EAGAIN
- 其他错误(如无效参数)返回
-1
,errno
设置为具体错误码
- 冲突锁存在时返回
-
示例:
struct flock lock = {0}; lock.l_type = F_WRLCK; // 加写锁 lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; // 锁定整个文件 if (fcntl(fd, F_SETLK, &lock) == -1) { perror("fcntl F_SETLK failed"); }
2.3 F_SETLKW: 阻塞等待加锁
-
功能 :类似于
F_SETLK
,但若存在冲突锁,会阻塞等待 直到锁被释放或被信号中断。 -
行为 :
- 成功加锁后返回
0
。 - 被信号中断时返回
-1
,errno = EINTR
- 成功加锁后返回
-
示例 :
struct flock lock = {0}; lock.l_type = F_RDLCK; // 加读锁 lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; // 锁定整个文件 if (fcntl(fd, F_SETLKW, &lock) == -1) { perror("fcntl F_SETLKW failed"); }
注意:F_SETLKW
不会填充 flock
结构体 ,它只是阻塞等待锁可用。填充结构体是 F_GETLK
的功能