FCNTL - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-08-13
名称
fcntl-操作文件描述符
语法
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
说明
fcntl()对打开的文件描述符fd执行以下描述的操作之一。该操作由cmd确定。
fcntl()可以采用可选的第三个参数。是否需要此参数由cmd确定。每个cmd名称后的括号中都指示必需的参数类型(在大多数情况下,必需的类型为int,我们使用名称arg标识参数),如果不需要该参数,则指定void。
仅从特定的Linux内核版本开始,才支持以下某些操作。检查主机内核是否支持特定操作的首选方法是使用所需的cmd值调用fcntl(),然后使用EINVAL测试调用是否失败,这表明内核无法识别该值。
Duplicating a file descriptor
- F_DUPFD(int)
- 使用大于或等于arg的编号最小的可用文件描述符复制文件描述符fd。这与dup2(2)不同,后者完全使用指定的文件描述符。
- 成功后,将返回新的文件描述符。
- 有关更多详细信息,请参见dup(2)。
- F_DUPFD_CLOEXEC(int; since Linux 2.6.24)
- 与F_DUPFD一样,但还要为重复文件描述符设置close-on-exec标志。指定此标志使程序可以避免执行额外的fcntl()F_SETFD操作来设置FD_CLOEXEC标志。有关为何使用此标志的解释,请参见open(2)中的O_CLOEXEC描述。
File descriptor flags
以下命令操纵与文件描述符关联的标志。当前,仅定义了一个这样的标志:FD_CLOEXEC,执行时关闭标志。如果将FD_CLOEXEC位置1,则在成功执行execve(2)期间将自动关闭文件描述符。 (如果execve(2)失败,则文件描述符保持打开状态。)如果未设置FD_CLOEXEC位,则文件描述符将在execve(2)上保持打开状态。
- F_GETFD(void)
- 返回(作为函数结果)文件描述符标志; arg被忽略。
- F_SETFD(int)
- 将文件描述符标志设置为arg指定的值。
在多线程程序中,使用fcntl()F_SETFD在另一个线程执行fork(2)加execve(2)的同时设置close-on-exec标志很容易受到竞争条件的影响,这种竞争条件可能会无意间将文件描述符泄漏到在子进程中执行的程序。有关详细信息和对该问题的补救措施,请参见open(2)中O_CLOEXEC标志的讨论。
File status flags
每个打开的文件描述都有某些关联的状态标志,这些标志由open(2)初始化,并可能由fcntl()修改。重复的文件描述符(由dup(2),fcntl(F_DUPFD),fork(2)等组成)引用相同的打开文件描述,因此共享相同的文件状态标志。
文件状态标志及其语义在open(2)中进行了描述。
- F_GETFL(void)
- 返回(作为函数结果)文件访问模式和文件状态标志; arg被忽略。
- F_SETFL(int)
- 将文件状态标志设置为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标志。不能更改O_DSYNC和O_SYNC标志。请参阅下面的错误。
Advisory record locking
Linux实现了POSIX标准化的传统("与过程相关的")UNIX记录锁。有关具有更好语义的特定于Linux的替代方案,请参见下面有关打开文件描述锁的讨论。
F_SETLK,F_SETLKW和F_GETLK用于获取,释放和测试记录锁(也称为字节范围,文件段或文件区域锁)的存在。第三个参数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 (set by F_GETLK and F_OFD_GETLK) */ ... };
此结构的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_len指定0的特殊含义是:锁定所有从l_whence和l_start指定的位置开始的字节,直到文件末尾,无论文件有多大。
POSIX.1-2001允许(但不要求)实现支持负的l_len值;如果l_len为负,则锁所描述的间隔将覆盖字节l_start + l_len,直至并包括l_start-1。从内核版本2.4.21和2.5.49开始,Linux支持此功能。
l_type字段可用于在文件上放置读取(F_RDLCK)或写入(F_WRLCK)锁。任何数量的进程都可以在文件区域上持有读取锁(共享锁),但是只有一个进程可以持有写锁(排他锁)。排他锁排除所有其他锁,包括共享锁和排他锁。单个进程只能在文件区域上持有一种类型的锁。如果将新锁应用于已经锁定的区域,则现有锁将转换为新锁类型。 (如果新锁指定的字节范围与现有锁的范围不完全一致,则此类转换可能涉及与现有锁的拆分,缩小或合并。)
- F_SETLK(struct flock *)
- 在锁的l_whence,l_start和l_len指定的字节上获取锁(当l_type为F_RDLCK或F_WRLCK时)或释放锁(当l_type为F_UNLCK时)。如果另一个进程持有冲突的锁,则此调用返回-1并将errno设置为EACCES或EAGAIN。 (这种情况下返回的错误在不同的实现中有所不同,因此POSIX需要可移植的应用程序来检查这两个错误。)
- F_SETLKW(struct flock *)
- 对于F_SETLK,但是如果文件上拥有冲突的锁,则等待该锁被释放。如果在等待时捕获到信号,则调用将中断,并且(在信号处理程序返回之后)立即返回(返回值-1和errno设置为EINTR;请参见signal(7))。
- F_GETLK(struct flock *)
- 输入此调用时,锁描述了我们要放置在文件上的锁。如果可以放置锁,则fcntl()不会实际放置它,而是在锁的l_type字段中返回F_UNLCK,并使该结构的其他字段保持不变。
- 如果一个或多个不兼容的锁将阻止放置此锁,则fcntl()返回有关锁的l_type,l_whence,l_start和l_len字段中的那些锁之一的详细信息。如果冲突锁是传统的(与进程相关的)记录锁,则将l_pid字段设置为持有该锁的进程的PID。如果冲突的锁是打开的文件描述锁,则将l_pid设置为-1。请注意,到调用者检查时,返回的信息可能已经过时。
为了放置读取锁,必须打开fd进行读取。为了放置写锁,必须打开fd进行写操作。要放置两种类型的锁,请以读写方式打开文件。
当使用F_SETLKW放置锁时,内核会检测到死锁,从而使两个或多个进程的锁请求被其他进程持有的锁相互阻塞。例如,假设进程A在文件的字节100上持有写锁定,进程B在字节200上持有写锁定。如果每个进程然后尝试使用F_SETLKW锁定另一个进程已经锁定的字节,则不会发生死锁检测时,两个进程将无限期保持阻塞状态。当内核检测到此类死锁时,它将导致阻塞锁请求之一立即失败,并显示错误EDEADLK。遇到此类错误的应用程序应释放其一些锁,以允许其他应用程序继续运行,然后再尝试重新获得所需的锁。还检测到涉及两个以上进程的循环死锁。但是请注意,内核的死锁检测算法存在局限性。参见错误。
除了由显式的F_UNLCK删除,记录锁还会在进程终止时自动释放。
记录锁不是通过fork(2)创建的子级继承的,而是在execve(2)中保留的。
由于stdio(3)库执行了缓冲操作,因此应避免在该程序包中使用例程进行记录锁定。使用read(2)和write(2)代替。
上述记录锁与该进程相关联(与下面描述的打开文件描述锁不同)。这有一些不幸的后果:
- *
- 如果某个进程关闭了引用该文件的任何文件描述符,则该进程在该文件上的所有锁都将被释放,而与获得该锁的文件描述符无关。这很不好:这意味着当某个库函数出于某种原因决定打开,读取和关闭同一文件时,进程可能会丢失对/ etc / passwd或/ etc / mtab等文件的锁定。
- *
- 进程中的线程共享锁。换句话说,多线程程序不能使用记录锁定来确保线程不会同时访问文件的同一区域。
打开文件描述锁可以解决这两个问题。
Open file description locks (non-POSIX)
打开文件描述锁是建议性字节范围锁,其操作在大多数方面与上述传统记录锁相同。此锁定类型特定于Linux,自Linux 3.15起可用。 (Austin Group提议在POSIX.1的下一修订版中包括此锁定类型。)有关打开文件描述的说明,请参见open(2)。
两种锁类型之间的主要区别在于,传统记录锁与进程相关联,而打开文件描述锁与在其上获取它们的打开文件描述相关联,就像使用flock(2)获取锁一样。因此(与传统的咨询记录锁不同),打开文件描述锁是在fork(2)(和带有CLONE_FILES的clone(2))中继承的,并且仅在打开文件描述的最后一次关闭时自动释放,而不是释放在文件的任何关闭位置。
冲突的锁组合(例如,读锁和写锁或两个写锁),其中一个锁是打开的文件描述锁,而另一个锁是传统的记录锁冲突,即使它们是通过同一文件描述符上的同一过程获取的。
通过相同的打开文件描述(即,通过相同的文件描述符,或通过fork(2),dup(2),fcntl()F_DUPFD等创建的文件描述符的副本)放置的打开文件描述锁是:始终兼容:如果将新锁放在已经锁定的区域上,则现有锁将转换为新锁类型。 (如上所述,此类转换可能导致与现有锁的分裂,缩小或合并。)
另一方面,通过不同的打开文件描述获取打开文件描述锁时,它们可能彼此冲突。因此,通过让每个线程在文件上执行其自己的open(2)并通过生成的文件描述符应用锁,多线程程序中的线程可以使用打开的文件描述锁来同步对文件区域的访问。
与传统的咨询锁一样,fcntl()的第三个参数lock是指向flock结构的指针。与传统的记录锁定相反,使用以下描述的命令时,该结构的l_pid字段必须设置为零。
使用打开的文件描述锁的命令类似于使用传统锁的命令:
- F_OFD_SETLK(struct flock *)
- 在由锁的l_whence,l_start和l_len指定的字节上,获取打开文件描述锁(当l_type为F_RDLCK或F_WRLCK时)或释放打开文件描述锁(当l_type为F_UNLCK时)。如果另一个进程持有冲突的锁,则此调用返回-1并将errno设置为EAGAIN。
- F_OFD_SETLKW(struct flock *)
- 对于F_OFD_SETLK,但是如果文件上拥有冲突的锁,则等待该锁被释放。如果在等待时捕获到信号,则调用将中断,并且(在信号处理程序返回之后)立即返回(返回值-1和errno设置为EINTR;请参见signal(7))。
- F_OFD_GETLK(struct flock *)
- 在输入此调用时,锁描述了我们要放置在文件上的打开的文件描述锁。如果可以放置锁,则fcntl()不会实际放置它,而是在锁的l_type字段中返回F_UNLCK,并使该结构的其他字段保持不变。如果一个或多个不兼容的锁将阻止放置此锁,则如上针对F_GETLK所述,通过锁返回有关这些锁之一的详细信息。
在当前的实现中,不对打开的文件描述锁执行死锁检测。 (这与进程相关的记录锁形成对比,内核确实对其执行死锁检测。)
Mandatory locking
警告:强制锁定的Linux实现不可靠。请参阅下面的错误。由于存在这些错误,并且人们认为该功能很少使用,因此从Linux 4.5开始,强制锁定已成为可选功能,由配置选项(CONFIG_MANDATORY_FILE_LOCKING)控制。这是完全删除此功能的第一步。
默认情况下,传统(与流程相关的)和打开文件描述记录锁都是建议性的。咨询锁未强制执行,仅在协作过程之间有用。
两种锁定类型也可以是必需的。强制锁适用于所有进程。如果某个进程尝试对具有不兼容强制性锁的文件区域执行不兼容访问(例如read(2)或write(2)),则结果取决于是否为其打开的文件描述启用了O_NONBLOCK标志。如果未启用O_NONBLOCK标志,则系统调用将被阻止,直到锁被删除或转换为与访问兼容的模式为止。如果启用了O_NONBLOCK标志,则系统调用将失败,并显示错误EAGAIN。
要使用强制锁定,必须在包含要锁定文件的文件系统上以及文件本身上启用强制锁定。使用mount(8)的" -o mand"选项或mount(2)的MS_MANDLOCK标志在文件系统上启用强制锁定。通过禁用文件的组执行许可权并启用set-group-ID许可权位,可以对文件启用强制锁定(请参阅chmod(1)和chmod(2))。
POSIX未指定强制锁定。其他一些系统也支持强制锁定,尽管如何启用它的细节因系统而异。
Lost locks
在网络文件系统(例如NFS)上获得咨询锁时,该锁可能会丢失。这可能是由于服务器上的管理操作或网络分区(即与服务器之间的网络连接断开)导致的,这种分区持续的时间足以使服务器假定客户端不再起作用。
当文件系统确定锁丢失时,将来的read(2)或write(2)请求可能会因错误EIO而失败。该错误将一直存在,直到取消锁定或关闭文件描述符为止。从Linux 3.12开始,这种情况至少发生在NFSv4(包括所有次要版本)上。
在这种情况下,某些版本的UNIX发送信号(SIGLOST)。 Linux没有定义该信号,并且不提供丢失锁的任何异步通知。
Managing signals
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以正值形式返回;进程组ID作为负值返回(但请参见下面的错误)。 arg被忽略。
- F_SETOWN(int)
- 设置将接收文件描述符fd上事件的SIGIO和SIGURG信号的进程ID或进程组ID。目标进程或进程组ID在arg中指定。进程ID被指定为正值;进程组ID被指定为负值。最常见的是,调用进程将自身指定为所有者(即arg被指定为getpid(2))。
- 除了设置文件描述符所有者外,还必须启用文件描述符上的信号生成。这可以通过使用fcntl()F_SETFL命令在文件描述符上设置O_ASYNC文件状态标志来完成。随后,只要在文件描述符上可能进行输入或输出,就发送SIGIO信号。 fcntl()F_SETSIG命令可用于获取SIGIO以外的信号的传递。
- 向F_SETOWN指定的所有者进程(组)发送信号要接受与kill(2)所述相同的权限检查,其中发送进程是采用F_SETOWN的进程(但请参见下面的BUGS)。如果此权限检查失败,则该信号将被静默丢弃。注意:F_SETOWN操作在fcntl()调用时记录调用者的凭据,并且这些保存的凭据用于权限检查。
- 如果文件描述符fd引用了套接字,则F_SETOWN还选择带外数据到达该套接字时传递的SIGURG信号的接收者。 (在select(2)将套接字报告为具有"异常情况"的任何情况下发送SIGURG。)
- The following was true in 2.6.x kernels up to and including
kernel 2.6.11:
- 如果在运行带有支持线程组(例如NPTL)的线程库的多线程进程中,给F_SETSIG一个非零值,那么给F_SETOWN的正值具有不同的含义:不是标识整个进程的进程ID,而是它是一个线程ID,用于标识进程中的特定线程。因此,当使用F_SETSIG时,可能有必要将gettid(2)的结果传递给F_SETOWN而不是getpid(2)以获得有意义的结果。 (在当前的Linux线程实现中,主线程的线程ID与进程ID相同。这意味着在这种情况下,单线程程序可以同等地使用gettid(2)或getpid(2)。)但是请注意,本段中的语句不适用于为套接字上的带外数据生成的SIGURG信号:此信号始终发送到进程或进程组,具体取决于赋予F_SETOWN的值。
- 上面的行为在Linux 2.6.12中被意外删除,将不会恢复。从Linux 2.6.32开始,使用F_SETOWN_EX将SIGIO和SIGURG信号定位到特定线程。
- F_GETOWN_EX(struct f_owner_ex *) (since Linux 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 *) (since Linux 2.6.32)
- This operation performs a similar task to
F_SETOWN.
It allows the caller to direct I/O availability signals
to a specific thread, process, or process group.
The caller specifies the target of signals via
arg,which is a pointer to a
f_owner_exstructure.
The
typefield has one of the following values, which define how
pidis interpreted:
- F_GETSIG(void)
- 当输入或输出变为可能时,返回(作为功能结果)发送的信号。零值表示已发送SIGIO。其他任何值(包括SIGIO)是发送的信号,在这种情况下,如果与SA_SIGINFO一起安装,则其他信息可用于信号处理程序。 arg被忽略。
- F_SETSIG(int)
- 将输入或输出变为可能时发送的信号设置为arg中给出的值。零值表示发送默认SIGIO信号。其他任何值(包括SIGIO)将代替发送信号,在这种情况下,如果与SA_SIGINFO一起安装,则其他信息可用于信号处理程序。
- 通过使用具有非零值的F_SETSIG并为信号处理程序设置SA_SIGINFO(请参见sigaction(2)),有关I / O事件的额外信息将以siginfo_t结构传递给处理程序。如果si_code字段指示源是SI_SIGIO,则si_fd字段提供与事件关联的文件描述符。否则,没有迹象表明哪些文件描述符正在挂起,您应该使用通常的机制(select(2),poll(2),read(2)并设置O_NONBLOCK等)来确定哪些文件描述符可用于I /哦
- 请注意,si_fd中提供的文件描述符是在F_SETSIG操作期间指定的文件描述符。这可能会导致异常情况。如果复制了文件描述符(dup(2)或类似文件),并且关闭了原始文件描述符,则将继续生成I / O事件,但是si_fd字段将包含现在关闭的文件描述符的编号。
- 通过选择实时信号(值>= SIGRTMIN),可以使用相同的信号编号将多个I / O事件排队。 (排队取决于可用内存。)如上所述,如果为信号处理程序设置了SA_SIGINFO,则可以使用其他信息。
- 请注意,Linux对可能排队到进程的实时信号的数量施加了限制(请参阅getrlimit(2)和signal(7)),如果达到此限制,则内核将恢复为传递SIGIO,并且信号传递到整个过程,而不是特定的线程。
使用这些机制,程序可以在大多数时间无需使用select(2)或poll(2)的情况下实现完全异步的I / O。
O_ASYNC的使用特定于BSD和Linux。 POSIX.1中指定的F_GETOWN和F_SETOWN的唯一用法是与套接字上SIGURG信号的用法结合使用。 (POSIX未指定SIGIO信号。)F_GETOWN_EX,F_SETOWN_EX,F_GETSIG和F_SETSIG特定于Linux。 POSIX具有异步I / O和aio_sigevent结构来实现相似的功能。这些在Linux中也作为GNU C库(Glibc)的一部分提供。
Leases
F_SETLEASE和F_GETLEASE(从Linux 2.4开始)用于在文件描述符fd引用的打开文件描述上建立新的租约,并检索当前的租约。文件租用提供了一种机制,当进程("租用中断者")尝试打开(2)或截断(2)进程时,通知持有租用的进程(通过信号传递)。该文件描述符所引用的文件。
- F_SETLEASE(int)
- Set or remove a file lease according to which of the following
values is specified in the integer
arg:
- F_RDLCK
- 取出阅读租约。这将导致在打开文件进行写入或截断文件时通知调用过程。只读租约只能放在以只读方式打开的文件描述符上。
- F_WRLCK
- 取出写租约。当打开文件进行读取或写入或被截断时,这将导致呼叫者收到通知。仅当文件没有其他打开的文件描述符时,才可以将写租约放置在文件上。
- F_UNLCK
- 从文件中删除我们的租约。
租赁与打开的文件描述相关联(请参见open(2))。这意味着重复的文件描述符(例如,由fork(2)或dup(2)创建)引用相同的租约,并且可以使用任何这些描述符来修改或释放此租约。此外,可以通过对这些重复文件描述符中的任何一个的显式F_UNLCK操作或在所有此类文件描述符都已关闭时释放租约。
租约只能在常规文件中提取。非特权进程只能在其UID(所有者)与进程的文件系统UID匹配的文件上租用租约。具有CAP_LEASE功能的进程可以租用任意文件的租约。
- F_GETLEASE(void)
- 通过返回F_RDLCK,F_WRLCK或F_UNLCK来指示与文件描述符fd关联的租约类型,分别表示读租约,写租约或无租约。 arg被忽略。
当某个进程(" lease breaker")执行与通过F_SETLEASE建立的租约冲突的open(2)或truncate(2)时,系统调用被内核阻止,并且内核通过向其发送信号来通知租约持有者(默认为SIGIO)。租约持有人应通过进行任何必要的清理工作来响应此信号的接收,以准备文件可以被另一个进程访问(例如,刷新缓存的缓冲区),然后删除或降级其租约。通过执行将arg指定为F_UNLCK的F_SETLEASE命令来删除租约。如果租约持有人当前在文件上拥有写租约,并且租约破坏者正在打开文件以进行读取,则租约持有人将租约降级为已读租约就足够了。通过执行将arg指定为F_RDLCK的F_SETLEASE命令来完成此操作。
如果租约持有人未能在/ proc / sys / fs / lease-break-time中指定的秒数内降级或删除租约,则内核将强制删除或降级租约持有人的租约。
启动租约中断后,F_GETLEASE返回目标租约类型(F_RDLCK或F_UNLCK,具体取决于与破坏租约者兼容的对象),直到租约持有者自愿降级或删除租约或内核在租约后强行这样做休息计时器到期。
一旦租约被自愿或强制删除或降级,并且假定租约破坏者未解除对系统调用的阻止,内核将允许租约破坏者的系统调用继续进行。
如果信号处理程序中断了租用断路器的阻塞的open(2)或truncate(2),则系统调用将失败,并显示错误EINTR,但是其他步骤仍然如上所述。如果租用断路器在open(2)或truncate(2)中被阻塞时被信号杀死,则其他步骤仍然如上所述。如果租用中断程序在调用open(2)时指定了O_NONBLOCK标志,则该调用立即失败,并显示错误EWOULDBLOCK,但是其他步骤仍然如上所述。
用来通知租约持有人的默认信号是SIGIO,但是可以使用F_SETSIG命令对fcntl()进行更改。如果执行了F_SETSIG命令(甚至是一个指定SIGIO的命令),并且使用SA_SIGINFO建立了信号处理程序,则该处理程序将收到siginfo_t结构作为其第二个参数,并且该参数的si_fd字段将保存租用文件的文件描述符已被另一个进程访问的文件。 (如果调用者针对多个文件持有租约,这将很有用。)
File and directory change notification (dnotify)
- F_NOTIFY(int)
- (从Linux 2.4开始)更改fd引用的目录或其中包含的任何文件时提供通知。待通知的事件在arg中指定,arg是通过对以下零个或多个以下位进行或运算来指定的位掩码:
- DN_ACCESS
- 已访问文件(read(2),pread(2),readv(2)等)
- DN_MODIFY
- 修改了文件(write(2),pwrite(2),writev(2),truncate(2),ftruncate(2)等)。
- DN_CREATE
- 创建了一个文件(此目录中的open(2),creat(2),mknod(2),mkdir(2),link(2),symlink(2),重命名(2))。
- DN_DELETE
- 取消链接文件(unlink(2),重命名(2)到另一个目录rmdir(2))。
- DN_RENAME
- 文件已在此目录中重命名(rename(2))。
- DN_ATTRIB
- 文件的属性已更改(chown(2),chmod(2),utime(2),utimensat(2)等)。
- (为了获得这些定义,必须在包含任何头文件之前定义_GNU_SOURCE功能测试宏。)
- 目录通知通常是"一次性"的,并且应用程序必须重新注册才能接收进一步的通知。或者,如果arg中包含DN_MULTISHOT,则通知将一直有效直到被明确删除。
- 一系列F_NOTIFY请求是累积性的,arg中的事件将添加到已监视的集合中。要禁用所有事件的通知,请进行F_NOTIFY调用,将arg指定为0。
- 通知通过信号传递发生。默认信号是SIGIO,但是可以使用F_SETSIG命令将其更改为fcntl()。 (请注意,SIGIO是非排队标准信号之一;切换为使用实时信号意味着可以将多个通知排队到该进程中。)在后一种情况下,信号处理程序将接收siginfo_t结构作为其第二个参数。 (如果处理程序是使用SA_SIGINFO建立的),并且此结构的si_fd字段包含生成通知的文件描述符(在多个目录上建立通知时很有用)。
- 尤其是在使用DN_MULTISHOT时,应使用实时信号进行通知,以便可以将多个通知排队。
- 注意:新应用程序应使用inotify接口(自内核2.6.13起可用),该接口为获取文件系统事件的通知提供了更为优越的接口。参见inotify(7)。
Changing the capacity of a pipe
- F_SETPIPE_SZ(int; since Linux 2.6.35)
- 将fd所指管道的容量更改为至少arg字节。无特权的进程可以将管道容量调整为系统页面大小和/ proc / sys / fs / pipe-max-size中定义的限制之间的任何值(请参阅proc(5))。尝试将管道容量设置为低于页面大小时,将自动舍入为页面大小。无特权的进程尝试将管道容量设置为超出/ proc / sys / fs / pipe-max-size中的限制,从而产生错误EPERM;特权进程(CAP_SYS_RESOURCE)可以覆盖该限制。
- 在为管道分配缓冲区时,如果方便实现,内核可以使用大于arg的容量。 (在当前实现中,分配是请求的大小的下一个较高的2分之二的页面大小的倍数。)所设置的实际容量(以字节为单位)作为函数结果返回。
- 尝试将管道容量设置为小于当前用于存储数据的缓冲区空间量时,将产生错误EBUSY。
- 注意,由于在将数据写入管道时采用管道缓冲区页面的方式,取决于写入的大小,可以写入的字节数可能小于标称大小。
- F_GETPIPE_SZ(void; since Linux 2.6.35)
- 返回(作为函数结果)fd引用的管道的容量。
File Sealing
文件密封限制了对给定文件的允许操作集。从现在开始,对于在文件上设置的每个印章,一组特定的操作将失败,并对该文件进行EPERM。据说文件是密封的。默认的密封集取决于底层文件和文件系统的类型。有关文件密封的概述,其用途的讨论以及一些代码示例,请参见memfd_create(2)。
当前,文件密封只能应用于memfd_create(2)返回的文件描述符(如果使用了MFD_ALLOW_SEALING)。在其他文件系统上,对印章进行操作的所有fcntl()操作都将返回EINVAL。
密封是inode的属性。因此,所有引用同一inode的打开文件描述符都共享同一组密封。此外,密封件永远不能删除,只能添加。
- F_ADD_SEALS(int; since Linux 3.17)
- 将位掩码参数arg中给出的密封添加到文件描述符fd引用的inode的密封集合中。密封垫不能再次拆除。一旦此调用成功,密封将立即由内核强制执行。如果当前的密封件集合包括F_SEAL_SEAL(请参见下文),则该调用将被EPERM拒绝。万一尚未设置F_SEAL_SEAL,则添加已设置的密封是不可操作的。为了加盖印章,文件描述符fd必须是可写的。
- F_GET_SEALS(void; since Linux 3.17)
- 返回(作为函数结果)fd所引用的inode的当前密封集。如果未设置密封件,则返回0。如果文件不支持密封,则返回-1并将errno设置为EINVAL。
提供以下密封件:
- F_SEAL_SEAL
- 如果设置了此密封,则使用F_ADD_SEALS进一步调用fcntl()都会失败,并显示错误EPERM。因此,该密封件防止对密封件组本身进行任何修改。如果文件的初始印章集包括F_SEAL_SEAL,则这将有效地使印章集保持恒定并被锁定。
- F_SEAL_SHRINK
- 如果设置了此印章,则无法减小该文件的大小。这会影响带有O_TRUNC标志的open(2)以及truncate(2)和ftruncate(2)。如果您尝试缩小有问题的文件,则这些调用将因EPERM而失败。仍然可以增加文件大小。
- F_SEAL_GROW
- 如果设置了此印章,则无法增加相关文件的大小。这会影响到文件结尾truncate(2),ftruncate(2)和fallocate(2)之外的write(2)。如果使用它们增加文件大小,这些调用将在EPERM上失败。如果您保留大小或缩小大小,这些调用仍将按预期工作。
- F_SEAL_WRITE
- 如果设置了此标记,则无法修改文件的内容。请注意,仍然可以缩小文件或增大文件大小。因此,该密封件通常与其他密封件之一组合使用。这种密封会影响write(2)和fallocate(2)(仅与FALLOC_FL_PUNCH_HOLE标志结合使用)。如果设置了此密封,则这些呼叫将因EPERM而失败。此外,尝试通过mmap(2)创建新的共享的,可写的内存映射也将因EPERM而失败。
- 如果存在任何可写的共享映射,则使用F_ADD_SEALS操作设置F_SEAL_WRITE密封失败并显示EBUSY。必须先取消映射此类映射,然后才能添加此图章。此外,如果文件上有任何待处理的异步I / O操作(io_submit(2)),则所有未完成的写操作都将被丢弃。
- F_SEAL_FUTURE_WRITE(since Linux 5.1)
- 此标记的效果类似于F_SEAL_WRITE,但仍可以通过在设置标记之前创建的共享可写映射来修改文件的内容。通过mmap(2)在文件上创建新的可写映射的任何尝试都将因EPERM而失败。同样,尝试通过write(2)写入文件将因EPERM而失败。
- 使用此标记,一个进程可以创建一个内存缓冲区,可以在与其他进程"只读"共享该缓冲区的同时继续对其进行修改。
File read/write hints
写生存期提示可用于通知内核有关给定索引节点上的相对预期写生存时间或通过特定的打开文件描述。 (有关打开的文件描述的说明,请参见open(2)。)在这种情况下,术语"写生存期"是指数据在被覆盖或擦除之前将在媒体上保留的预期时间。
应用程序可以使用下面指定的不同提示值将写入分为不同的写入类,以便在单个存储后端上运行的多个用户或应用程序可以一致的方式聚合其I / O模式。但是,这些标志没有暗示任何功能语义,并且不同的I / O类可以以任意方式使用写入生存期提示,只要这些提示被一致地使用即可。
以下操作可以应用于文件描述符fd:
- F_GET_RW_HINT(uint64_t *; since Linux 4.13)
- 返回与fd引用的基础inode关联的读/写提示的值。
- F_SET_RW_HINT(uint64_t *; since Linux 4.13)
- 设置与fd引用的基础索引节点关联的读/写提示值。该提示一直持续到显式修改或卸载基础文件系统为止。
- F_GET_FILE_RW_HINT(uint64_t *; since Linux 4.13)
- 返回与fd引用的打开文件描述关联的读/写提示的值。
- F_SET_FILE_RW_HINT(uint64_t *; since Linux 4.13)
- 设置与fd引用的打开文件描述关联的读/写提示值。
如果尚未为打开的文件描述分配读/写提示,则它将使用分配给索引节点的值(如果有)。
从Linux 4.13开始,以下读/写提示有效:
- RWH_WRITE_LIFE_NOT_SET
- 尚未设置任何具体提示。这是默认值。
- RWH_WRITE_LIFE_NONE
- 没有特定的写生存期与此文件或索引节点相关联。
- RWH_WRITE_LIFE_SHORT
- 写入此索引节点或通过此打开的文件描述写入的数据预期寿命很短。
- RWH_WRITE_LIFE_MEDIUM
- 预期写入此inode或通过此打开的文件描述的数据的生存期比使用RWH_WRITE_LIFE_SHORT写入的数据更长。
- RWH_WRITE_LIFE_LONG
- 预期写入此inode或通过此打开的文件描述的数据的生存期比使用RWH_WRITE_LIFE_MEDIUM写入的数据更长。
- RWH_WRITE_LIFE_EXTREME
- 与该RWH_WRITE_LIFE_LONG写入的数据相比,写入此inode或通过此打开的文件描述的数据的预期寿命更长。
所有特定于写的提示都是相对的,并且不应将任何单独的绝对含义归因于它们。
返回值
对于成功的调用,返回值取决于以下操作:
- F_DUPFD
- 新的文件描述符。
- F_GETFD
- 文件描述符标志的值。
- F_GETFL
- 文件状态标志的值。
- F_GETLEASE
- 文件描述符上保留的租约类型。
- F_GETOWN
- 文件描述符所有者的值。
- F_GETSIG
- 当可能进行读取或写入时发送的信号值,对于传统的SIGIO行为为零。
- F_GETPIPE_SZ, F_SETPIPE_SZ
- 管道容量。
- F_GET_SEALS
- 位掩码,标识已为fd引用的inode设置的密封。
- All other commands
- 零。
如果出错,则返回-1,并正确设置errno。
错误说明
- EACCESor EAGAIN
- 其他进程持有的锁禁止操作。
- EAGAIN
- 禁止该操作,因为该文件已被另一个进程进行内存映射。
- EBADF
- fd不是打开的文件描述符
- EBADF
- cmd为F_SETLK或F_SETLKW,并且文件描述符打开模式与请求的锁定类型不匹配。
- EBUSY
- cmd为F_SETPIPE_SZ,并且arg中指定的新管道容量小于当前用于在管道中存储数据的缓冲区空间量。
- EBUSY
- cmd是F_ADD_SEALS,arg包含F_SEAL_WRITE,并且在fd引用的文件上存在可写的共享映射。
- EDEADLK
- 检测到指定的F_SETLKW命令将导致死锁。
- EFAULT
- 锁在您可访问的地址空间之外。
- EINTR
- cmd为F_SETLKW或F_OFD_SETLKW且操作已被信号中断;参见signal(7)。
- EINTR
- cmd为F_GETLK,F_SETLK,F_OFD_GETLK或F_OFD_SETLK,并且在检查或获取锁定之前,该操作已被信号中断。锁定远程文件时最有可能发生(例如,通过NFS锁定),但有时可能会在本地发生。
- EINVAL
- 此内核无法识别在cmd中指定的值。
- EINVAL
- cmd为F_ADD_SEALS,而arg包含无法识别的密封位。
- EINVAL
- cmd是F_ADD_SEALS或F_GET_SEALS,并且包含fd引用的inode的文件系统不支持密封。
- EINVAL
- cmd为F_DUPFD,arg为负或大于最大允许值(请参阅getrlimit(2)中有关RLIMIT_NOFILE的讨论)。
- EINVAL
- cmd是F_SETSIG,而arg是不允许的信号编号。
- EINVAL
- cmd为F_OFD_SETLK,F_OFD_SETLKW或F_OFD_GETLK,并且l_pid未指定为零。
- EMFILE
- cmd为F_DUPFD,并且已达到打开文件描述符数量的每个进程限制。
- ENOLCK
- 段锁打开太多,锁表已满或远程锁定协议失败(例如,通过NFS锁定)。
- ENOTDIR
- 在cmd中指定了F_NOTIFY,但fd不引用目录。
- EPERM
- cmd为F_SETPIPE_SZ,并且已达到用户管道的软硬限制。参见pipe(7)。
- EPERM
- 尝试清除设置了仅附加属性的文件上的O_APPEND标志。
- EPERM
- cmd为F_ADD_SEALS,但fd尚未打开以供写入,或者文件上的当前印章集已包含F_SEAL_SEAL。
遵循规范
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或更大的_XOPEN_SOURCE或值为200809L或更大的_POSIX_C_SOURCE。)
在POSIX.1-2008中指定了F_DUPFD_CLOEXEC。 (要获得此定义,请定义_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宏以获得这些定义。)
F_OFD_SETLK,F_OFD_SETLKW和F_OFD_GETLK是Linux特定的(必须定义_GNU_SOURCE才能获得它们的定义),但是正在努力将它们包含在下一版本的POSIX.1中。
F_ADD_SEALS和F_GET_SEALS特定于Linux。
备注
dup2(2)返回的错误与F_DUPFD返回的错误不同。
File locking
最初的Linux fcntl()系统调用未设计为处理大文件偏移(在flock结构中)。因此,在Linux 2.4中添加了fcntl64()系统调用。较新的系统调用对文件锁定flock64和相应的命令F_GETLK64,F_SETLK64和F_SETLKW64采用了不同的结构。但是,使用glibc的应用程序可以忽略这些详细信息,该应用程序的fcntl()包装函数透明地使用可用的最新系统调用。
Record locks
从内核2.0开始,由flock(2)和fcntl()放置的锁的类型之间没有相互作用。
几个系统在struct flock中具有更多字段,例如l_sysid(用于标识持有锁的机器)。显然,如果持有锁的进程可能驻留在不同的计算机上,那么仅使用l_pid并不是很有用。在Linux上,虽然在某些体系结构(例如MIPS32)上存在,但未使用此字段。
最初的Linux fcntl()系统调用未设计为处理大文件偏移(在flock结构中)。因此,在Linux 2.4中添加了fcntl64()系统调用。较新的系统调用对文件锁定flock64和相应的命令F_GETLK64,F_SETLK64和F_SETLKW64采用了不同的结构。但是,使用glibc的应用程序可以忽略这些详细信息,该应用程序的fcntl()包装函数透明地使用可用的最新系统调用。
Record locking and NFS
在Linux 3.12之前,如果NFSv4客户端失去与服务器的连接一段时间(定义为90秒以上,没有通信),则它可能会丢失并重新获得锁,而不会意识到这一事实。 (假设失去联系的时间段称为NFSv4租约时间。在Linux NFS服务器上,可以通过查看/ proc / fs / nfsd / nfsv4leasetime来确定,该时间以秒为单位。默认值此文件的最大文件数为90。)由于另一进程可能在此期间获得了锁定并执行文件I / O,因此此方案潜在地有可能损坏数据。
从Linux 3.12开始,如果NFSv4客户端失去与服务器的连接,则"认为"该文件持有锁的进程对该文件的任何I / O都会失败,直到该进程关闭并重新打开该文件。可以将内核参数nfs.recover_lost_locks设置为1,以获取3.12之前的行为,借此,当与服务器重新建立联系时,客户端将尝试恢复丢失的锁。由于随之而来的数据损坏风险,此参数默认为0(禁用)。
BUGS
F_SETFL
不能使用F_SETFL更改O_DSYNC和O_SYNC标志的状态。尝试更改这些标志的状态将被忽略。
F_GETOWN
Linux系统调用约定在某些体系结构(尤其是i386)上的局限性意味着,如果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问题不可见。
F_SETOWN
在Linux 2.4和更早版本中,当非特权进程使用F_SETOWN将套接字文件描述符的所有者指定为调用者以外的进程(组)时,可能会发生错误。在这种情况下,即使所有者进程(组)是调用者有权向其发送信号的进程,fcntl()也可以将errno设置为EPERM来返回-1。尽管返回了此错误,但文件描述符所有者已设置,并且信号将发送到所有者。
Deadlock detection
内核在处理F_SETLKW请求时采用的死锁检测算法会同时产生假否定(检测死锁失败,使一组死锁进程无限期地被阻塞)和假肯定(无死锁时会产生EDEADLK错误)。例如,内核将其依赖关系搜索的锁定深度限制为10步,这意味着将不会检测到超过该大小的循环死锁链。此外,当使用clone(2)CLONE_FILES标志创建的两个或更多进程放置(出现在内核上)冲突的内核时,内核可能错误地指示死锁。
Mandatory locking
Linux强制性锁定的实现受制于竞争条件,这使其变得不可靠:与锁定重叠的write(2)调用在获取强制性锁定后可能会修改数据;与锁重叠的read(2)调用可能会检测到仅在获得写锁之后才对数据进行的更改。强制锁和mmap(2)之间也存在类似的竞争。因此,不建议依靠强制性锁定。
另外参见
dup2(2),flock(2),open(2),socket(2),lockf(3),功能(7),feature_test_macros(7),lslocks(8)
Linux内核源目录Documentation / filesystems /中的locks.txt,manually-locking.txt和dnotify.txt(在较早的内核上,这些文件直接位于Documentation /目录下,commended-locking.txt称为manual.txt )
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。