> 技术文档 > 【Linux 编程】:深入解析 fcntl 函数_fcntl函数

【Linux 编程】:深入解析 fcntl 函数_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 以指示错误类型

常见的命令包括:

  1. 复制一个现有的描述符

    • F_DUPFD:复制文件描述符
    • F_DUPFD_CLOEXEC:复制文件描述符,设置FD_CLOEXEC标志
  2. 获得/设置文件状态标记

    • F_GETFL:获取文件描述符的当前状态标志
    • F_SETFL:设置文件描述符的状态标志
  3. 获得/设置文件描述符标记

    • F_GETFD:获取文件描述符的内部标志
      F_SETFD:设置文件描述符的内部标志
  4. 获得/设置异步I/O所有权

    • F_GETOWN:获得异步I/O所有权
    • F_SETOWN:设置异步I/O所有权
  5. 获得/设置记录锁
    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. 二者区别

  • dupF_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\\n\", (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\\n\", (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=0printf(\"fdFlag=%d, fcntlFdFlag=%d, fcntlCloFdFlag=%d, dupFdFlag=%d\\n\",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_GETFDF_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 标志可以通过三个方法得到:

  1. 调用 open 函数是,指定 O_CLOEXEC
  2. 通过 fcntl 函数使用 F_DUPFD_CLOEXEC 复制文件描述符,新的描述符就是 FD_CLOEXEC
  3. 通过 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=1printf(\"flagFd=%d, flagFdCloExec=%d, flagFdCloExecDup=%d flagFdSetFd=%d\\n\", flagFd,flagFdCloExec,flagFdCloExecDup,flagFdSetFd);close(fd);close(fdCloExec);close(fdCloExecDup);close(fdSetFd);return 0;}

分析

  • flagFd=0fd 通过 open(...) 创建时未设置 O_CLOEXEC,标志未启用
  • flagFdCloExec=1fdCloExecopen 时指定了 O_CLOEXEC,标志已设置
  • flagFdCloExecDup=1F_DUPFD_CLOEXEC 自动设置 FD_CLOEXEC
  • flagFdSetFd=1dup 后通过 F_SETFD 显式设置标志

五、获取/设置文件状态标志

🛠 fcntl 函数中的 F_GETFLF_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_RDONLYO_WRONLYO_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),因为它锁定的只是文件中的一个区域(也可能是整个文件)
  1. 字节范围锁(Byte-range Locking)
    • UNIX/Linux 的记录锁本质是对文件的某个字节范围加锁 ,而非传统意义上的“记录”。
    • 例如:锁定文件从偏移 100200 字节的区域,其他进程不能修改该区域。
  2. 锁的类型
    • 读锁(F_RDLCK :共享锁,允许多个进程同时读取锁定区域,但阻止写操作。
    • 写锁(F_WRLCK :独占锁,阻止其他进程读写锁定区域。
    • 解锁(F_UNLCK :释放已持有的锁。
  3. 锁的继承与释放
    • 进程终止时,所有锁自动释放。
    • fork() 子进程不会继承父进程的锁。
    • execve() 后,若文件描述符设置了 FD_CLOEXEC,锁会被释放。

F_SETLKF_SETLKWF_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_whencel_startl_len这三个参数用于分段对文件加锁,若对整个文件加锁,则:l_whence= SEEK_SET,l_start= 0,l_len= 0
  • l_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; // 锁定前 1KBif (fcntl(fd, F_GETLK, &lock) == 0) { if (lock.l_type == F_UNLCK) { printf(\"No conflicting lock.\\n\"); } else { printf(\"Conflicting lock by PID %d\\n\", lock.l_pid); }}
2.2 F_SETTLK: 尝试加锁/解锁
  • 功能 :尝试对指定区域加锁或解锁。

  • 行为

    • 加锁 :若无冲突,立即加锁;若有冲突,返回 -1errno 设置为 EACCESEAGAIN
    • 解锁 :直接释放指定区域的锁
  • 错误处理

    • 冲突锁存在时返回 -1errno = EACCES/EAGAIN
    • 其他错误(如无效参数)返回 -1errno 设置为具体错误码
  • 示例

    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
    • 被信号中断时返回 -1errno = 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 的功能

【Linux 编程】:深入解析 fcntl 函数_fcntl函数