Linux flock和fcntl函数详解

文章目录

flock函数

主要功能是在已打开的文件应用或者删除共享锁或者独占锁。sys/file.h声明了这个函数,声明的原型是:int flock(int fd, int operation)

描述

  1. fd是文件描述符。operation可以是下列参数之一:

    1. LOCK_SH:放置共享锁。多个进程可能在给定时间持有给定文件的共享锁。
    2. LOCK_EX :放置独占锁。在给定时间只有一个进程可以持有给定文件的排他锁。
    3. LOCK_UN:删除此进程持有的现有锁。
  2. 如果另一个进程持有不兼容的锁,则对 freeze() 的调用可能会阻塞。要发出非阻塞请求,请在上述任何操作中包含 LOCK_NB(通过 ORing)。

  3. 单个文件不能同时拥有共享锁和排它锁。

  4. flock()创建的锁与打开的文件表条目相关联。这意味着重复的文件描述符(例如,由 fork 或 dup 创建的)引用相同的锁,并且可以使用这些描述符中的任何一个来修改或释放该锁。此外,锁可以通过对任何这些重复描述符执行显式 LOCK_UN 操作来释放,或者当所有此类描述符都已关闭时释放。

  5. 如果进程使用 open (或类似的)来获取同一文件的多个描述符,则这些描述符将由 flock() 独立处理。使用这些文件描述符之一锁定文件的尝试可能会被调用进程已经通过另一个描述符放置的锁拒绝。

  6. 进程只能在文件上持有一种类型的锁(共享或独占)。后续对已锁定文件的flock() 调用会将现有锁定转换为新的锁定模式。

  7. 由flock() 创建的锁在execve 中保留。

  8. 无论文件打开的模式如何,都可以对文件放置共享锁或独占锁。

返回值和错误码

  1. 成功后,返回零。出错时,返回 -1,并适当设置 errno
  2. EBADF:fd 不是打开的文件描述符。
  3. EINTR:在等待获取锁时,调用被处理程序捕获的信号的传递中断;参见信号signals相关文档。
  4. EINVAL:操作无效。
  5. ENOLCK:内核内存不足,无法分配锁定记录。
  6. EWOULDBLOCK:文件被锁定并选择了 LOCK_NB 标志。

笔记

  1. flock() 调用首次出现在BSD4.2中。在大多数 UNIX 系统上都出现了一个 flock() 版本,可能是根据 fcntl() 实现的。
  2. flock() 不会锁定 NFS 上的文件。使用 fcntl() 代替。如果 Linux 版本足够新并且服务器支持锁定,它确实可以通过 NFS 工作。
  3. 从内核 2.0 开始,flock() 本身被实现为系统调用,而不是在 GNU C 库中模拟为对 fcntl() 的调用。这产生了真正的BSD语义:flock()fcntl()放置的锁类型之间没有交互,并且flock()不检测死锁。
  4. flock() 放置的锁不具有强约束力。实现了协议的软件才支持(比如SSH和PUTTY)。它给定文件的适当权限,进程可以自由地忽略flock()的使用并在文件上执行I/O。
  5. 对于fork()进程和dup()flock()fcntl() 锁具有不同的语义。在使用fcntl() 实现flock() 的系统上,flock() 的语义将与Linux手册中描述的语义不同。
  6. 转换锁(共享锁到独占锁,或反之亦然)不保证是原子(线程安全)的:首先删除现有锁,然后建立新锁。在这两个步骤之间,可能会授予另一个进程的挂起锁定请求,结果是转换要么阻塞,要么在指定 LOCK_NB 的情况下失败。 这是原始的 BSD 行为,并且发生在许多其他实现中。

fcntl函数

操作文件描述符的函数。两个头文件须引入:

c 复制代码
#include <unistd.h>
#include <fcntl.h>

函数的声明原型是int fcntl(int fd, int cmd, ... /* arg */ );

描述

fcntl() 对打开的文件描述符 fd 执行各种操作。fcntl() 可以采用可选的第三个参数。操作指令由cmd决定,操作内容因硬件的差异而不同。是否需要此参数由 cmd 决定。所需的参数类型在每个 cmd 名称后面的括号中指示(大多数情况下,所需的类型是 int,我们使用名称 arg 来标识参数),如果不需要参数,则指定 void

复制文件描述符

  1. F_DUPFD:找到大于或等于arg的最小编号的可用文件描述符,并将其作为fd的副本。这与 dup2 不同,后者完全使用指定的描述符。有关dup2()的差异请查看相关文档。成功后,将返回新的描述符。
  2. F_DUPFD_CLOEXEC:与 F_DUPFD 一样,还为重复描述符设置 close-on-exec 标志。指定此标志允许程序避免额外的 fcntl() F_SETFD 操作来设置 FD_CLOEXEC 标志。有关此标志为何有用的说明,请参阅 open()O_CLOEXEC 的描述。

文件描述标志

以下命令操作与文件描述符关联的标志。目前,只定义了一个这样的标志:FD_CLOEXECclose-on-exec 标志。如果 FD_CLOEXEC 位为 0,文件描述符将在 execve() 期间保持打开状态,否则将被关闭。

  1. F_GETFD:读取文件描述符标志; arg 被忽略。
  2. F_SETFD:将文件描述符标志设置为 arg 指定的值。

文件状态标志

每个打开的文件描述都有某些关联的状态标志,由 open() 初始化,并可能由 fcntl() 修改。重复的文件描述符(使用 dup()fcntl(F_DUPFD)fork() 等创建)引用相同的打开文件描述,因此共享相同的文件状态标志。文件状态标志及其语义在 open() 相关文档中有描述。

  1. F_GETFL:获取文件访问模式和文件状态标志; arg 被忽略。
  2. F_SETFL:将文件状态标志设置为 arg 指定的值。 arg 中的文件访问模式(O_RDONLYO_WRONLYO_RDWR)和文件创建标志(即 O_CREATO_EXCLO_NOCTTYO_TRUNC)将被忽略。在 Linux 上,此命令只能更改 O_APPENDO_ASYNCO_DIRECTO_NOATIMEO_NONBLOCK 标志。

咨询锁

主要用于flock()函数。F_GETLKF_SETLKF_SETLKW 用于获取、释放和测试记录锁(也称为文件段锁或文件区域锁)是否存在。第三个参数 lock 是一个指向结构的指针,该结构至少具有以下字段(按未指定的顺序)。

C 复制代码
struct flock {
    ...
    short l_type;    /* Type of lock: F_RDLCK,
                        F_WRLCK, F_UNLCK */
    short l_whence;  /* How to interpret l_start:
                        SEEK_SET, SEEK_CUR, SEEK_END */
    off_t l_start;   /* Starting offset for lock */
    off_t l_len;     /* Number of bytes to lock */
    pid_t l_pid;     /* PID of process blocking our lock
                        (F_GETLK only) */
    ...
};

该结构的 l_whencel_startl_len 字段指定我们希望锁定的字节范围。文件末尾之后的字节可能会被锁定,但文件开头之前的字节不会被锁定。

l_start 是锁的起始偏移量,并相对于以下任一位置进行解释: 文件的开头(如果 l_whenceSEEK_SET);当前文件偏移量(如果 l_whenceSEEK_CUR);或文件末尾(如果 l_whenceSEEK_END)。在最后两种情况下,l_start 可以是负数,前提是偏移量不在文件开头之前。

l_len 指定要锁定的字节数。如果l_len为正数,则要锁定的范围涵盖字节l_start直到l_start+l_len-1(包括l_start+l_len-1)。为 l_len 指定 0 具有特殊含义:锁定从 l_whencel_start 指定的位置开始到文件末尾的所有字节,无论文件增长多大。

POSIX.1-2001 允许(但不要求)实现支持负 l_len 值;如果 l_len 为负数,则 lock 描述的间隔涵盖字节 l_start+l_len 直至(包括 l_start-1)。 Linux 从内核版本 2.4.21 和 2.5.49 开始支持这一点。

l_type 字段可用于在文件上放置读 (F_RDLCK) 或写 (F_WRLCK) 锁。任意数量的进程都可以在一个文件区域上持有读锁(共享锁),但只有一个进程可以持有写锁(独占锁)。独占锁排除所有其他锁,包括共享锁和独占锁。单个进程只能在文件区域上持有一种类型的锁;如果将新锁应用于已锁定区域,则现有锁将转换为新锁类型。 (如果新锁指定的字节范围与现有锁的范围不完全一致,则此类转换可能涉及拆分、收缩或与现有锁合并。)

  1. F_SETLK (struct flock *)

    lockl_whencel_startl_len字段指定的字节获取锁(当l_typeF_RDLCKF_WRLCK时)或释放锁(当l_typeF_UNLCK时)。如果冲突的锁被另一个进程持有,则此调用返回 -1 并将 errno 设置为 EACCESEAGAIN

  2. F_SETLKW (struct flock *)

    F_SETLK 一样,但如果文件上持有冲突锁,则等待该锁被释放。如果在等待时捕获到信号,则调用将被中断并(在信号处理程序返回后)立即返回(返回值 -1 且 errno 设置为 EINTR;请查阅 signal())。

  3. F_GETLK (struct flock *)

    在输入此调用时,lock 描述了我们想要在文件上放置的锁。如果可以放置锁,则 fcntl() 实际上不会放置它,而是在 lockl_type 字段中返回 F_UNLCK 并保持结构的其他字段不变。如果一个或多个不兼容的锁会阻止放置此锁,则 fcntl() 将在 lockl_typel_whencel_startl_len 字段中返回有关这些锁之一的详细信息,并将 l_pid 设置为持有该锁的进程的 PID

为了放置读锁,fd 必须打开以供读取。为了放置写锁,fd 必须打开以进行写入。要放置两种类型的锁,请以读写方式打开文件。

除了通过显式 F_UNLCK 删除记录锁之外,当进程终止或关闭任何引用持有锁的文件的文件描述符时,记录锁也会自动释放。这很糟糕:这意味着当由于某种原因库函数决定打开、读取和关闭文件(例如 /etc/passwd/etc/mtab)时,进程可能会失去对文件的锁定。

记录锁不会由通过 fork() 创建的子进程继承,而是在 execve() 中保留。由于 stdio 库执行缓冲,因此应避免在该包中的例程中使用记录锁定;使用 read()write() 代替。

强制锁

本章不符合POSIX规范。上述记录锁可以是建议性的,也可以是强制的,并且默认情况下是建议性的。咨询锁不强制执行,仅在协作进程之间有用。对所有进程强制执行强制锁。如果进程尝试对具有不兼容强制锁的文件区域执行不兼容访问(例如,read()write()),则结果取决于是否为其打开的文件描述启用了 O_NONBLOCK 标志。如果未启用 O_NONBLOCK 标志,则系统调用将被阻止,直到锁被移除或转换为与访问兼容的模式。如果启用了 O_NONBLOCK 标志,则系统调用将失败并出现错误 EAGAIN

要使用强制锁定,必须在包含要锁定的文件的文件系统和文件本身上启用强制锁定。使用 mount() 的"-o mand"选项或 mount()MS_MANDLOCK 标志在文件系统上启用强制锁定。通过禁用文件的组执行权限并启用 set-group-ID 权限位(请参阅 chmod()chmod()),可以对文件启用强制锁定。Linux 的强制锁定实现是不可靠的。请参阅下文详细说明的错误。

管理信号

F_GETOWNF_SETOWNF_GETOWN_EXF_SETOWN_EXF_GETSIGF_SETSIG 用于管理 I/O 可用性信号。

  1. F_GETOWN (void)

    返回(作为函数结果)当前接收文件描述符 fd 上事件的 SIGIOSIGURG 信号的进程 ID 或进程组。进程 ID 以正值形式返回;进程组 ID 作为负值返回(但请参阅下面的错误)。 arg 被忽略。

  2. F_SETOWN (int)

    将文件描述符 fd 上的事件接收 SIGIOSIGURG 信号的进程 ID 或进程组 ID 设置为 arg 中给定的 ID。进程ID指定为正值;进程组 ID 指定为负值。最常见的是,调用进程将自身指定为所有者(即 arg 指定为 getpid())。

    如果使用 fcntl()F_SETFL 命令在文件描述符上设置 O_ASYNC 状态标志,则只要该文件描述符上可以进行输入或输出,就会发送 SIGIO 信号。 F_SETSIG 可用于获取 SIGIO 以外的信号传递。如果此权限检查失败,则该信号将被默默丢弃。

    F_SETOWN 指定的所有者进程(组)发送信号会受到与 Kill() 中所述相同的权限检查,其中发送进程是使用 F_SETOWN 的进程(但请参阅下面的错误)。

    如果文件描述符 fd 引用套接字,则 F_SETOWN 还会选择当带外数据到达该套接字时传送的 SIGURG 信号的接收者。 (SIGURGselect() 报告套接字有"异常情况"的任何情况下都会发送。)

    以下情况在 2.6.x 内核直至(包括)内核 2.6.11 中均成立:

    如果在使用支持线程组(例如 NPTL)的线程库运行的多线程进程中为 F_SETSIG 赋予非零值,则为 F_SETOWN 赋予正值具有不同的含义:而不是标识整个进程的进程 ID,它是标识进程内特定线程的线程 ID。因此,当使用 F_SETSIG 时,可能需要将 gettid() 的结果而不是 getpid() 传递给 F_SETOWN 以获得合理的结果。 (在当前的 Linux 线程实现中,主线程的线程 ID 与其进程 ID 相同。这意味着单线程程序在这种情况下同样可以使用 gettid()getpid()。)但是请注意,本段中的语句不适用于为套接字上的带外数据生成的 SIGURG 信号:此信号始终发送到进程或进程组,具体取决于为 F_SETOWN 指定的值。

    上述行为在 Linux 2.6.12 中被意外删除,并且不会恢复。 从 Linux 2.6.32 开始,使用 F_SETOWN_EXSIGIOSIGURG 信号定位到特定线程。

  3. F_GETOWN_EX (struct f_owner_ex *)(内核2.6.32版本开始提供)

    返回由先前的 F_SETOWN_EX 操作定义的当前文件描述符所有者设置。信息在 arg 指向的结构中返回,其形式如下:

    C 复制代码
    struct f_owner_ex {
        int   type;
        pid_t pid;
    };

    类型字段将具有值 F_OWNER_TIDF_OWNER_PIDF_OWNER_PGRP 之一。 pid 字段是一个正整数,表示线程 ID、进程 ID 或进程组 ID。有关详细信息,请参阅 F_SETOWN_EX

  4. F_SETOWN_EX (struct f_owner_ex *) (内核2.6.32版本开始提供)

    此操作执行与 F_SETOWN 类似的任务。它允许调用者将 I/O 可用性信号定向到特定的线程、进程或进程组。调用者通过 arg 指定信号的目标,arg 是指向 f_owner_ex 结构的指针。 type 字段具有以下值之一,它们定义如何解释 pid

    1. F_OWNER_TID

      将信号发送到 pid 中指定了线程 ID(调用 clone()gettid() 返回的值)的线程。

    2. F_OWNER_PID

      向 pid 中指定 ID 的进程发送信号。

    3. F_OWNER_PGRP

      向pid中指定ID的进程组发送信号。 (请注意,与 F_SETOWN 不同,此处将进程组 ID 指定为正值。)

  5. F_GETSIG (void)

    返回(作为函数结果)当输入或输出成为可能时发送的信号。值为零表示发送了 SIGIO。任何其他值(包括 SIGIO)都是发送的信号,在这种情况下,如果与 SA_SIGINFO 一起安装,则信号处理程序可以使用附加信息。 arg 被忽略。

  6. F_SETSIG (int)

    将输入或输出变为可能时发送的信号设置为 arg 中给定的值。值为零表示发送默认的 SIGIO 信号。任何其他值(包括 SIGIO)都是要发送的信号,在这种情况下,如果与 SA_SIGINFO 一起安装,则信号处理程序可以使用附加信息。

    通过使用具有非零值的 F_SETSIG,并为信号处理程序设置 SA_SIGINFO(请参阅 sigaction()),有关 I/O 事件的额外信息将传递到 siginfo_t 结构中的处理程序。如果 si_code 字段指示源是 SI_SIGIO,则 si_fd 字段给出与事件关联的文件描述符。否则,没有指示哪些文件描述符正在挂起,您应该使用常用的机制( select()poll()read()O_NONBLOCK 设置等)来确定哪些文件描述符可用于 I/O。

    通过选择实时信号(值 >= SIGRTMIN),可以使用相同的信号编号对多个 I/O 事件进行排队。 (排队取决于可用内存)。如果为信号处理程序设置了 SA_SIGINFO,则可获得额外信息,如上所述。

    请注意,Linux 对可以排队到进程的实时信号数量施加了限制(请参阅 getrlimit()signal()),如果达到此限制,则内核将恢复传递 SIGIO,并且这信号被传递到整个进程而不是特定的线程。

使用这些机制,程序可以实现完全异步 I/O,而无需在大多数情况下使用 select()poll()

O_ASYNCF_GETOWNF_SETOWN 的使用特定于 BSD 和 Linux。 F_GETOWN_EXF_SETOWN_EXF_GETSIGF_SETSIG 是 Linux 特定的。 POSIX 有异步 I/O 和 aio_sigevent 结构来实现类似的事情;这些也可以作为 GNU C 库 (Glibc) 的一部分在 Linux 中使用。

租赁

F_SETLEASEF_GETLEASE(Linux 2.4 及以上版本)分别用于在文件描述符 fd 引用的打开文件描述上建立新租约并检索当前租约。文件租约提供了一种机制,当进程("租约破坏者")尝试open()truncate()文件时,持有租约的进程("租约持有者")会收到通知(通过发送信号)。该文件描述符引用的文件。

  1. F_SETLEASE:根据整数 arg 中指定的以下值设置或删除文件租约:

    1. F_RDLCK:取出已读租约。这将导致调用进程在打开文件进行写入或被截断时收到通知。读租约只能放置在以只读方式打开的文件描述符上。
    2. F_WRLCK:拿出写租约。这将导致调用者在打开文件进行读取或写入或被截断时收到通知。仅当该文件没有其他打开的文件描述符时,才可以对该文件放置写租约。
    3. F_UNLCK:从文件中删除我们的租约。

    租约与打开的文件描述相关联(请参阅 open())。这意味着重复的文件描述符(例如,由 fork()dup()创建的)引用相同的租约,并且可以使用这些描述符中的任何一个来修改或释放该租约。此外,通过对任何这些重复描述符执行显式 F_UNLCK 操作,或者当所有此类描述符都已关闭时,可以释放租约。

    租赁只能在常规文件上进行。非特权进程只能对 UID(所有者)与该进程的文件系统 UID 匹配的文件进行租约。具有 CAP_LEASE 功能的进程可以获取任意文件的租约。

  2. F_GETLEASE (void):通过返回 F_RDLCKF_WRLCKF_UNLCK 来指示与文件描述符 fd 关联的租约类型,分别指示读租约、写租约或无租约。 arg 被忽略。

    当进程("租约破坏者")执行与通过 F_SETLEASE 建立的租约冲突的 open()truncate() 时,系统调用将被内核阻止,并且内核通过向租约持有者发送信号来通知租约持有者(默认为 SIGIO)。租约持有者应通过执行任何所需的清理操作来响应此信号的接收,以准备文件被另一个进程访问(例如,刷新缓存的缓冲区),然后删除或降级其租约。通过执行 F_SETLEASE 命令并将 arg 指定为 F_UNLCK 来删除租约。如果租约持有者当前持有该文件的写租约,并且租约破坏者正在打开该文件进行读取,则租约持有者将租约降级为读租约就足够了。这是通过执行 F_SETLEASE 命令并将 arg 指定为 F_RDLCK 来完成的。

    如果租约持有者未能在 /proc/sys/fs/lease-break-time 中指定的秒数内降级或删除租约,则内核将强制删除或降级租约持有者的租约。

    一旦启动租约中断,F_GETLEASE 将返回目标租约类型(F_RDLCKF_UNLCK,具体取决于与租约中断器兼容的类型),直到租约持有者自愿降级或删除租约,或者内核在租约结束后强制执行此操作休息定时器到期。

    一旦租约被自愿或强制删除或降级,并且假设租约破坏者尚未解除其系统调用的阻塞,内核将允许租约破坏者的系统调用继续进行。

    如果租约中断器阻塞的 open()truncate() 被信号处理程序中断,则系统调用将失败并出现错误 EINTR,但其他步骤仍然会发生,如上所述。如果租约破坏者在 open()truncate() 中被阻塞时被信号杀死,则其他步骤仍然会发生,如上所述。如果租约破坏者在调用 open() 时指定了 O_NONBLOCK 标志,则调用会立即失败并出现错误 EWOULDBLOCK,但其他步骤仍然会按照上面的描述进行。

    用于通知租约持有者的默认信号是 SIGIO,但可以使用 fcntl()F_SETSIG 命令更改此信号。如果执行 F_SETSIG 命令(即使指定 SIGIO),并且使用 SA_SIGINFO 建立信号处理程序,则处理程序将接收 siginfo_t 结构作为其第二个参数,并且该参数的 si_fd 字段将保存租用文件的描述符已被另一个进程访问。 (如果调用者持有多个文件的租约,这非常有用)。

文件和目录变更通知

F_NOTIFY (int)(Linux 2.4 及以上)当 fd 引用的目录或其包含的任何文件发生更改时提供通知。要通知的事件在 arg 中指定,arg 是通过对以下零个或多个位进行或运算指定的位掩码:

  1. DN_ACCESS:文件被访问(readpreadreadv
  2. DN_MODIFY:文件被修改(writepwritewritevtruncateftruncate)。
  3. DN_CREATE:创建了一个文件(open, creat, mknod, mkdir, link, symlink, rename)。
  4. DN_DELETE:文件被取消链接(unlinkrename到另一个目录,rmdir)。
  5. DN_RENAME:该目录中的文件被重命名(重命名)
  6. DN_ATTRIB:文件的属性已更改(chownchmodutime[s])。

为了获得这些定义,必须在包含任何头文件之前定义 _GNU_SOURCE 功能测试宏。

一系列 F_NOTIFY 请求是累积的,arg 中的事件被添加到已监视的集合中。要禁用所有事件的通知,请进行 F_NOTIFY 调用并将 arg 指定为 0。

通知是通过信号的传递发生的。默认信号是 SIGIO,但可以使用 fcntl()F_SETSIG 命令进行更改。在后一种情况下,信号处理程序接收 siginfo_t 结构作为其第二个参数(如果处理程序是使用 SA_SIGINFO 建立的),并且该结构的 si_fd 字段包含生成通知的文件描述符(在多个目录上建立通知时有用)。

特别是在使用 DN_MULTISHOT 时,应该使用实时信号进行通知,以便可以将多个通知排队。注意:新应用程序应使用 inotify 接口(自内核 2.6.13 起可用),它提供了一个更高级的接口来获取文件系统事件的通知。请参阅 inotify 相关资料。

改变管道容量

  1. F_SETPIPE_SZ (int);从Linux 2.6.35版本开始提供。将 fd 引用的管道的容量更改为至少为 arg 字节。非特权进程可以将管道容量调整为系统页面大小和 /proc/sys/fs/pipe-max-size 中定义的限制之间的任何值(请参阅 proc())。尝试将管道容量设置为低于页面大小时会自动四舍五入到页面大小。非特权进程尝试将管道容量设置为高于 /proc/sys/fs/pipe-max-size 中的限制会产生错误 EPERM;特权进程 (CAP_SYS_RESOURCE) 可以覆盖该限制。当为管道分配缓冲区时,如果方便实现,内核可以使用大于arg的容量。 F_GETPIPE_SZ 操作返回实际使用的大小。尝试将管道容量设置为小于当前用于存储数据的缓冲区空间量会产生错误 EBUSY
  2. F_GETPIPE_SZ(void)从Linux 2.6.35版本开始提供。返回(作为函数结果)fd 引用的管道的容量。

返回值

对于成功的调用,返回值取决于操作:

  1. F_DUPFD:新的描述符。
  2. F_GETFD:文件描述符标志的值。
  3. F_GETFL:文件状态标志的值。
  4. F_GETLEASE:文件描述符上持有的租约类型。
  5. F_GETOWN:描述符所有者的值。
  6. F_GETSIG:当可以进行读取或写入时发送的信号值,或者对于传统的 SIGIO 行为为零。
  7. F_GETPIPE_SZ:管道容量。
  8. 所有其它命令:0表示没有错误。出错时,返回 -1,并适当设置 errno

错误

  1. EACCESEAGAIN:操作被其他进程持有的锁禁止。
  2. EAGAIN:该操作被禁止,因为该文件已被另一个进程进行内存映射。
  3. EBADFfd 不是打开的文件描述符,或者命令是 F_SETLKF_SETLKW 并且文件描述符打开模式与请求的锁定类型不匹配。
  4. EDEADLK:检测到指定的 F_SETLKW 命令会导致死锁。
  5. EFAULT:锁位于您可访问的地址空间之外。
  6. EINTR:对于F_SETLKW,命令被信号中断;参见signal相关资料。对于 F_GETLKF_SETLK,在检查或获取锁之前命令被信号中断。最有可能发生在锁定远程文件时(例如,通过 NFS 锁定),但有时也可能发生在本地。
  7. EINVAL:对于 F_DUPFDarg 为负数或大于最大允许值。对于 F_SETSIGarg 不是允许的信号号。
  8. EMFILE:对于F_DUPFD,进程已经打开了最大数量的文件描述符。
  9. ENOLCK:打开太多段锁、锁表已满或远程锁定协议失败(例如,通过 NFS 锁定)。
  10. EPERM:尝试清除设置了仅附加属性的文件上的 O_APPEND 标志。

备注

  1. SVr4、4.3BSD、POSIX.1-2001。 POSIX.1-2001 中仅指定了操作 F_DUPFDF_GETFDF_SETFDF_GETFLF_SETFLF_GETLKF_SETLKF_SETLKW
  2. F_GETOWNF_SETOWN 在 POSIX.1-2001 中指定。 (要获取它们的定义,请使用 500 或更大的值定义 BSD_SOURCE_XOPEN_SOURCE,或者使用 200809L 或更大的值定义 _POSIX_C_SOURCE。)
  3. F_DUPFD_CLOEXEC 在 POSIX.1-2008 中指定。 (要获得此定义,请将 _POSIX_C_SOURCE 定义为 200809L 或更大的值,或者将 _XOPEN_SOURCE 定义为 700 或更大的值。)
  4. F_GETOWN_EXF_SETOWN_EXF_SETPIPE_SZF_GETPIPE_SZF_GETSIGF_SETSIGF_NOTIFYF_GETLEASEF_SETLEASE 是 Linux 特定的。 (定义 _GNU_SOURCE 宏来获取这些定义。)
  5. 最初的 Linux fcntl() 系统调用并不是为了处理大文件偏移量(在集群结构中)而设计的。因此,Linux 2.4 中添加了 fcntl() 系统调用。较新的系统调用采用不同的文件锁定结构,flock 和相应的命令 F_GETLK64F_SETLK64F_SETLKW64。然而,使用 glibc 的应用程序可以忽略这些细节,其 fcntl() 包装函数透明地使用可用的更新的系统调用。
  6. dup2 返回的错误与 F_DUPFD 返回的错误不同。
  7. 从内核 2.0 开始,flock()fcntl() 放置的锁类型之间没有交互。
  8. 一些系统在 structflock 中具有更多字段,例如 l_sysid。显然,如果持有锁的进程可能位于不同的机器上,那么单独的 l_pid 不会很有用。

遗留问题

  1. 某些体系结构(尤其是 i386)上的 Linux 系统调用约定的限制意味着,如果 F_GETOWN 返回的(负)进程组 ID 落在 -1 到 -4095 的范围内,则返回值会被 glibc 错误地解释为系统调用出错;也就是说,fcntl() 的返回值为-1,并且 errno 将包含(正)进程组 ID。 Linux 特有的 F_GETOWN_EX 操作避免了这个问题。从 glibc 2.11 版开始,glibc 通过使用 F_GETOWN_EX 实现 F_GETOWN 来使内核 F_GETOWN 问题不可见。
  2. 在 Linux 2.4 及更早版本中,当非特权进程使用 F_SETOWN 将套接字文件描述符的所有者指定为调用者之外的进程(组)时,可能会出现错误。在这种情况下,fcntl() 可以返回 -1,并将 errno 设置为 EPERM,即使调用者有权向其发送信号的所有者进程(组)也是如此。尽管返回此错误,但文件描述符所有者已设置,并且信号将发送给所有者。
  3. 所有已知版本的 Linux 中强制锁定的实现都受到竞争条件的影响,这导致其不可靠:与锁定重叠的 write() 调用可能会在获取强制锁定后修改数据;与锁重叠的 read() 调用可能会检测仅在获取写锁后对数据所做的更改。强制锁和 mmap() 之间也存在类似的竞争。因此,不建议依赖强制锁定。

|-----|-------------------------------|
| 作者: | 岬淢箫声 |
| 日期: | 2023年11月3日 |
| 版本: | 1.0 |
| 链接: | http://caowei.blog.csdn.net |

相关推荐
小能喵几秒前
Kali Linux Wifi 伪造热点
linux·安全·kali·kali linux
汀沿河15 分钟前
8.1 prefix Tunning与Prompt Tunning模型微调方法
linux·运维·服务器·人工智能
zly350028 分钟前
centos7 ping127.0.0.1不通
linux·运维·服务器
小哥山水之间1 小时前
基于dropbear实现嵌入式系统ssh服务端与客户端完整交互
linux
ldj20201 小时前
2025 Centos 安装PostgreSQL
linux·postgresql·centos
翻滚吧键盘2 小时前
opensuse tumbleweed上安装显卡驱动
linux
liulilittle2 小时前
深度剖析:OPENPPP2 libtcpip 实现原理与架构设计
开发语言·网络·c++·tcp/ip·智能路由器·tcp·通信
cui_win2 小时前
【内存】Linux 内核优化实战 - net.ipv4.tcp_tw_reuse
linux·网络·tcp/ip
十年编程老舅3 小时前
跨越十年的C++演进:C++20新特性全解析
c++·c++11·c++20·c++14·c++23·c++17·c++新特性