文章目录
- flock函数
- 描述
- 返回值和错误码
- 笔记
- fcntl函数
- 描述
- 复制文件描述符
- 文件描述标志
- 文件状态标志
- 咨询锁
- 强制锁
- 管理信号
- 租赁
- 文件和目录变更通知
- 改变管道容量
- 返回值
- 错误
- 备注
- 遗留问题
flock函数
主要功能是在已打开的文件应用或者删除共享锁或者独占锁。sys/file.h
声明了这个函数,声明的原型是:int flock(int fd, int operation)
。
描述
-
fd
是文件描述符。operation
可以是下列参数之一:LOCK_SH
:放置共享锁。多个进程可能在给定时间持有给定文件的共享锁。LOCK_EX
:放置独占锁。在给定时间只有一个进程可以持有给定文件的排他锁。LOCK_UN
:删除此进程持有的现有锁。
-
如果另一个进程持有不兼容的锁,则对
freeze()
的调用可能会阻塞。要发出非阻塞请求,请在上述任何操作中包含LOCK_NB
(通过 ORing)。 -
单个文件不能同时拥有共享锁和排它锁。
-
由
flock()
创建的锁与打开的文件表条目相关联。这意味着重复的文件描述符(例如,由 fork 或 dup 创建的)引用相同的锁,并且可以使用这些描述符中的任何一个来修改或释放该锁。此外,锁可以通过对任何这些重复描述符执行显式 LOCK_UN 操作来释放,或者当所有此类描述符都已关闭时释放。 -
如果进程使用
open
(或类似的)来获取同一文件的多个描述符,则这些描述符将由flock()
独立处理。使用这些文件描述符之一锁定文件的尝试可能会被调用进程已经通过另一个描述符放置的锁拒绝。 -
进程只能在文件上持有一种类型的锁(共享或独占)。后续对已锁定文件的
flock()
调用会将现有锁定转换为新的锁定模式。 -
由flock() 创建的锁在
execve
中保留。 -
无论文件打开的模式如何,都可以对文件放置共享锁或独占锁。
返回值和错误码
- 成功后,返回零。出错时,返回 -1,并适当设置
errno
。 EBADF
:fd 不是打开的文件描述符。EINTR
:在等待获取锁时,调用被处理程序捕获的信号的传递中断;参见信号signals相关文档。EINVAL
:操作无效。ENOLCK
:内核内存不足,无法分配锁定记录。EWOULDBLOCK
:文件被锁定并选择了LOCK_NB
标志。
笔记
flock()
调用首次出现在BSD4.2中。在大多数 UNIX 系统上都出现了一个flock()
版本,可能是根据fcntl()
实现的。flock()
不会锁定 NFS 上的文件。使用fcntl()
代替。如果 Linux 版本足够新并且服务器支持锁定,它确实可以通过 NFS 工作。- 从内核 2.0 开始,
flock()
本身被实现为系统调用,而不是在GNU C
库中模拟为对fcntl()
的调用。这产生了真正的BSD语义:flock()
和fcntl()
放置的锁类型之间没有交互,并且flock()
不检测死锁。 flock()
放置的锁不具有强约束力。实现了协议的软件才支持(比如SSH和PUTTY)。它给定文件的适当权限,进程可以自由地忽略flock()
的使用并在文件上执行I/O。- 对于
fork()
进程和dup()
,flock()
和fcntl()
锁具有不同的语义。在使用fcntl()
实现flock()
的系统上,flock()
的语义将与Linux手册中描述的语义不同。 - 转换锁(共享锁到独占锁,或反之亦然)不保证是原子(线程安全)的:首先删除现有锁,然后建立新锁。在这两个步骤之间,可能会授予另一个进程的挂起锁定请求,结果是转换要么阻塞,要么在指定
LOCK_NB
的情况下失败。 这是原始的 BSD 行为,并且发生在许多其他实现中。
fcntl函数
操作文件描述符的函数。两个头文件须引入:
#include <unistd.h>
#include <fcntl.h>
函数的声明原型是int fcntl(int fd, int cmd, ... /* arg */ );
描述
fcntl() 对打开的文件描述符 fd 执行各种操作。fcntl()
可以采用可选的第三个参数。操作指令由cmd
决定,操作内容因硬件的差异而不同。是否需要此参数由 cmd
决定。所需的参数类型在每个 cmd
名称后面的括号中指示(大多数情况下,所需的类型是 int
,我们使用名称 arg
来标识参数),如果不需要参数,则指定 void
。
复制文件描述符
F_DUPFD
:找到大于或等于arg
的最小编号的可用文件描述符,并将其作为fd
的副本。这与dup2
不同,后者完全使用指定的描述符。有关dup2()
的差异请查看相关文档。成功后,将返回新的描述符。F_DUPFD_CLOEXEC
:与F_DUPFD
一样,还为重复描述符设置close-on-exec
标志。指定此标志允许程序避免额外的fcntl()
F_SETFD
操作来设置FD_CLOEXEC
标志。有关此标志为何有用的说明,请参阅open()
中O_CLOEXEC
的描述。
文件描述标志
以下命令操作与文件描述符关联的标志。目前,只定义了一个这样的标志:FD_CLOEXEC
,close-on-exec
标志。如果 FD_CLOEXEC
位为 0,文件描述符将在 execve()
期间保持打开状态,否则将被关闭。
F_GETFD
:读取文件描述符标志;arg
被忽略。F_SETFD
:将文件描述符标志设置为arg
指定的值。
文件状态标志
每个打开的文件描述都有某些关联的状态标志,由 open()
初始化,并可能由 fcntl()
修改。重复的文件描述符(使用 dup()
、fcntl(F_DUPFD)
、fork()
等创建)引用相同的打开文件描述,因此共享相同的文件状态标志。文件状态标志及其语义在 open()
相关文档中有描述。
F_GETFL
:获取文件访问模式和文件状态标志;arg
被忽略。F_SETFL
:将文件状态标志设置为arg
指定的值。arg
中的文件访问模式(O_RDONLY
、O_WRONLY
、O_RDWR
)和文件创建标志(即O_CREAT
、O_EXCL
、O_NOCTTY
、O_TRUNC
)将被忽略。在 Linux 上,此命令只能更改O_APPEND
、O_ASYNC
、O_DIRECT
、O_NOATIME
和O_NONBLOCK
标志。
咨询锁
主要用于flock()函数。F_GETLK
、F_SETLK
和 F_SETLKW
用于获取、释放和测试记录锁(也称为文件段锁或文件区域锁)是否存在。第三个参数 lock
是一个指向结构的指针,该结构至少具有以下字段(按未指定的顺序)。
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_whence
、l_start
和 l_len
字段指定我们希望锁定的字节范围。文件末尾之后的字节可能会被锁定,但文件开头之前的字节不会被锁定。
l_start
是锁的起始偏移量,并相对于以下任一位置进行解释: 文件的开头(如果 l_whence
为 SEEK_SET
);当前文件偏移量(如果 l_whence
为 SEEK_CUR
);或文件末尾(如果 l_whence
为 SEEK_END
)。在最后两种情况下,l_start
可以是负数,前提是偏移量不在文件开头之前。
l_len
指定要锁定的字节数。如果l_len
为正数,则要锁定的范围涵盖字节l_start
直到l_start+l_len-1
(包括l_start+l_len-1
)。为 l_len
指定 0 具有特殊含义:锁定从 l_whence
和 l_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
) 锁。任意数量的进程都可以在一个文件区域上持有读锁(共享锁),但只有一个进程可以持有写锁(独占锁)。独占锁排除所有其他锁,包括共享锁和独占锁。单个进程只能在文件区域上持有一种类型的锁;如果将新锁应用于已锁定区域,则现有锁将转换为新锁类型。 (如果新锁指定的字节范围与现有锁的范围不完全一致,则此类转换可能涉及拆分、收缩或与现有锁合并。)
-
F_SETLK (struct flock *)
对
lock
的l_whence
、l_start
和l_len
字段指定的字节获取锁(当l_type
为F_RDLCK
或F_WRLCK
时)或释放锁(当l_type
为F_UNLCK
时)。如果冲突的锁被另一个进程持有,则此调用返回 -1 并将errno
设置为EACCES
或EAGAIN
。 -
F_SETLKW (struct flock *)
与
F_SETLK
一样,但如果文件上持有冲突锁,则等待该锁被释放。如果在等待时捕获到信号,则调用将被中断并(在信号处理程序返回后)立即返回(返回值 -1 且errno
设置为EINTR
;请查阅signal()
)。 -
F_GETLK (struct flock *)
在输入此调用时,
lock
描述了我们想要在文件上放置的锁。如果可以放置锁,则fcntl()
实际上不会放置它,而是在lock
的l_type
字段中返回F_UNLCK
并保持结构的其他字段不变。如果一个或多个不兼容的锁会阻止放置此锁,则fcntl()
将在lock
的l_type
、l_whence
、l_start
和l_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_GETOWN
、F_SETOWN
、F_GETOWN_EX
、F_SETOWN_EX
、F_GETSIG
和 F_SETSIG
用于管理 I/O 可用性信号。
-
F_GETOWN (void)
返回(作为函数结果)当前接收文件描述符 fd 上事件的
SIGIO
和SIGURG
信号的进程 ID 或进程组。进程 ID 以正值形式返回;进程组 ID 作为负值返回(但请参阅下面的错误)。arg
被忽略。 -
F_SETOWN (int)
将文件描述符fd
上的事件接收SIGIO
和SIGURG
信号的进程 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 信号的接收者。(SIGURG
在select()
报告套接字有“异常情况”的任何情况下都会发送。)
以下情况在 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_EX
将SIGIO
和SIGURG
信号定位到特定线程。 -
F_GETOWN_EX (struct f_owner_ex *)
(内核2.6.32版本开始提供)返回由先前的
F_SETOWN_EX
操作定义的当前文件描述符所有者设置。信息在arg
指向的结构中返回,其形式如下:struct f_owner_ex { int type; pid_t pid; };
类型字段将具有值
F_OWNER_TID
、F_OWNER_PID
或F_OWNER_PGRP
之一。 pid 字段是一个正整数,表示线程 ID、进程 ID 或进程组 ID。有关详细信息,请参阅F_SETOWN_EX
。 -
F_SETOWN_EX (struct f_owner_ex *)
(内核2.6.32版本开始提供)此操作执行与
F_SETOWN
类似的任务。它允许调用者将 I/O 可用性信号定向到特定的线程、进程或进程组。调用者通过arg
指定信号的目标,arg
是指向f_owner_ex
结构的指针。type
字段具有以下值之一,它们定义如何解释 pid-
F_OWNER_TID
将信号发送到 pid 中指定了线程 ID(调用
clone()
或gettid()
返回的值)的线程。 -
F_OWNER_PID
向 pid 中指定 ID 的进程发送信号。
-
F_OWNER_PGRP
向pid中指定ID的进程组发送信号。 (请注意,与 F_SETOWN 不同,此处将进程组 ID 指定为正值。)
-
-
F_GETSIG (void)
返回(作为函数结果)当输入或输出成为可能时发送的信号。值为零表示发送了
SIGIO
。任何其他值(包括SIGIO
)都是发送的信号,在这种情况下,如果与SA_SIGINFO
一起安装,则信号处理程序可以使用附加信息。 arg 被忽略。 -
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_ASYNC
、F_GETOWN
、F_SETOWN
的使用特定于 BSD 和 Linux。 F_GETOWN_EX
、F_SETOWN_EX
、F_GETSIG
和 F_SETSIG
是 Linux 特定的。 POSIX 有异步 I/O 和 aio_sigevent
结构来实现类似的事情;这些也可以作为 GNU C 库 (Glibc) 的一部分在 Linux 中使用。
租赁
F_SETLEASE
和 F_GETLEASE
(Linux 2.4 及以上版本)分别用于在文件描述符 fd
引用的打开文件描述上建立新租约并检索当前租约。文件租约提供了一种机制,当进程(“租约破坏者”)尝试open()
或truncate()
文件时,持有租约的进程(“租约持有者”)会收到通知(通过发送信号)。该文件描述符引用的文件。
-
F_SETLEASE
:根据整数arg
中指定的以下值设置或删除文件租约:F_RDLCK
:取出已读租约。这将导致调用进程在打开文件进行写入或被截断时收到通知。读租约只能放置在以只读方式打开的文件描述符上。F_WRLCK
:拿出写租约。这将导致调用者在打开文件进行读取或写入或被截断时收到通知。仅当该文件没有其他打开的文件描述符时,才可以对该文件放置写租约。F_UNLCK
:从文件中删除我们的租约。
租约与打开的文件描述相关联(请参阅open()
)。这意味着重复的文件描述符(例如,由fork()
或dup()
创建的)引用相同的租约,并且可以使用这些描述符中的任何一个来修改或释放该租约。此外,通过对任何这些重复描述符执行显式F_UNLCK
操作,或者当所有此类描述符都已关闭时,可以释放租约。
租赁只能在常规文件上进行。非特权进程只能对 UID(所有者)与该进程的文件系统 UID 匹配的文件进行租约。具有CAP_LEASE
功能的进程可以获取任意文件的租约。 -
F_GETLEASE (void)
:通过返回F_RDLCK
、F_WRLCK
或F_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_RDLCK
或F_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 是通过对以下零个或多个位进行或运算指定的位掩码:
DN_ACCESS
:文件被访问(read
、pread
、readv
)DN_MODIFY
:文件被修改(write
、pwrite
、writev
、truncate
、ftruncate
)。DN_CREATE
:创建了一个文件(open
,creat
,mknod
,mkdir
,link
,symlink
,rename
)。DN_DELETE
:文件被取消链接(unlink
,rename
到另一个目录,rmdir
)。DN_RENAME
:该目录中的文件被重命名(重命名)DN_ATTRIB
:文件的属性已更改(chown
、chmod
、utime[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
相关资料。
改变管道容量
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
。F_GETPIPE_SZ(void)
从Linux 2.6.35版本开始提供。返回(作为函数结果)fd 引用的管道的容量。
返回值
对于成功的调用,返回值取决于操作:
F_DUPFD
:新的描述符。F_GETFD
:文件描述符标志的值。F_GETFL
:文件状态标志的值。F_GETLEASE
:文件描述符上持有的租约类型。F_GETOWN
:描述符所有者的值。F_GETSIG
:当可以进行读取或写入时发送的信号值,或者对于传统的SIGIO
行为为零。F_GETPIPE_SZ
:管道容量。- 所有其它命令:0表示没有错误。出错时,返回 -1,并适当设置
errno
。
错误
EACCES
或EAGAIN
:操作被其他进程持有的锁禁止。EAGAIN
:该操作被禁止,因为该文件已被另一个进程进行内存映射。EBADF
:fd
不是打开的文件描述符,或者命令是F_SETLK
或F_SETLKW
并且文件描述符打开模式与请求的锁定类型不匹配。EDEADLK
:检测到指定的F_SETLKW
命令会导致死锁。EFAULT
:锁位于您可访问的地址空间之外。EINTR
:对于F_SETLKW
,命令被信号中断;参见signal
相关资料。对于F_GETLK
和F_SETLK
,在检查或获取锁之前命令被信号中断。最有可能发生在锁定远程文件时(例如,通过 NFS 锁定),但有时也可能发生在本地。EINVAL
:对于F_DUPFD
,arg
为负数或大于最大允许值。对于F_SETSIG
,arg
不是允许的信号号。EMFILE
:对于F_DUPFD
,进程已经打开了最大数量的文件描述符。ENOLCK
:打开太多段锁、锁表已满或远程锁定协议失败(例如,通过 NFS 锁定)。EPERM
:尝试清除设置了仅附加属性的文件上的O_APPEND
标志。
备注
- SVr4、4.3BSD、POSIX.1-2001。 POSIX.1-2001 中仅指定了操作
F_DUPFD
、F_GETFD
、F_SETFD
、F_GETFL
、F_SETFL
、F_GETLK
、F_SETLK
和F_SETLKW
。 F_GETOWN
和F_SETOWN
在 POSIX.1-2001 中指定。 (要获取它们的定义,请使用 500 或更大的值定义BSD_SOURCE
或_XOPEN_SOURCE
,或者使用 200809L 或更大的值定义_POSIX_C_SOURCE
。)F_DUPFD_CLOEXEC
在 POSIX.1-2008 中指定。 (要获得此定义,请将_POSIX_C_SOURCE
定义为 200809L 或更大的值,或者将_XOPEN_SOURCE
定义为 700 或更大的值。)F_GETOWN_EX
、F_SETOWN_EX
、F_SETPIPE_SZ
、F_GETPIPE_SZ
、F_GETSIG
、F_SETSIG
、F_NOTIFY
、F_GETLEASE
和F_SETLEASE
是 Linux 特定的。 (定义_GNU_SOURCE
宏来获取这些定义。)- 最初的 Linux
fcntl()
系统调用并不是为了处理大文件偏移量(在集群结构中)而设计的。因此,Linux 2.4 中添加了fcntl()
系统调用。较新的系统调用采用不同的文件锁定结构,flock
和相应的命令F_GETLK64
、F_SETLK64
和F_SETLKW64
。然而,使用 glibc 的应用程序可以忽略这些细节,其fcntl()
包装函数透明地使用可用的更新的系统调用。 dup2
返回的错误与F_DUPFD
返回的错误不同。- 从内核 2.0 开始,
flock()
和fcntl()
放置的锁类型之间没有交互。 - 一些系统在
structflock
中具有更多字段,例如l_sysid
。显然,如果持有锁的进程可能位于不同的机器上,那么单独的l_pid
不会很有用。
遗留问题
- 某些体系结构(尤其是 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
问题不可见。 - 在 Linux 2.4 及更早版本中,当非特权进程使用
F_SETOWN
将套接字文件描述符的所有者指定为调用者之外的进程(组)时,可能会出现错误。在这种情况下,fcntl()
可以返回 -1,并将errno
设置为EPERM
,即使调用者有权向其发送信号的所有者进程(组)也是如此。尽管返回此错误,但文件描述符所有者已设置,并且信号将发送给所有者。 - 所有已知版本的 Linux 中强制锁定的实现都受到竞争条件的影响,这导致其不可靠:与锁定重叠的
write()
调用可能会在获取强制锁定后修改数据;与锁重叠的read()
调用可能会检测仅在获取写锁后对数据所做的更改。强制锁和mmap()
之间也存在类似的竞争。因此,不建议依赖强制锁定。
作者: | 岬淢箫声 |
日期: | 2023年11月3日 |
版本: | 1.0 |
链接: | http://caowei.blog.csdn.net |