OPEN - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-06-09
名称
open, openat, creat -打开并可能创建文件
语法
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); int openat(int dirfd, const char *pathname, int flags); int openat(int dirfd, const char *pathname, int flags, mode_t mode); /* Documented separately, in openat2(2): */ int openat2(int dirfd, const char *pathname, const struct open_how *how, size_t size);
glibc的功能测试宏要求(请参阅feature_test_macros(7)):
openat():
- Since glibc 2.10:
- _POSIX_C_SOURCE>= 200809L
- Before glibc 2.10:
- _ATFILE_SOURCE
说明
open()系统调用将打开由路径名指定的文件。如果指定的文件不存在,则可以选择通过open()创建它(如果在标志中指定了O_CREAT)。
open()的返回值是一个文件描述符,它是一个小的非负整数,在后续系统调用(read(2),write(2),lseek(2),fcntl(2)等)中使用)到打开的文件。成功调用返回的文件描述符将是当前未为该进程打开的编号最小的文件描述符。
默认情况下,新文件描述符设置为在execve(2)上保持打开状态(即,最初禁用了fcntl(2)中描述的FD_CLOEXEC文件描述符标志);如下所述,O_CLOEXEC标志可用于更改此默认值。文件偏移量设置为文件的开头(请参见lseek(2))。
调用open()会创建一个新的打开文件描述,这是系统范围的打开文件表中的一项。打开的文件描述记录了文件偏移量和文件状态标志(请参见下文)。文件描述符是对打开文件描述的引用;如果随后删除或修改了路径名以引用另一个文件,则此引用不受影响。有关打开文件描述的更多详细信息,请参见"注意"。
参数标志必须包含以下访问模式之一:O_RDONLY,O_WRONLY或O_RDWR。这些请求分别打开文件为只读,只写或读/写。
另外,零个或多个文件创建标志和文件状态标志可以在标志中按位或。文件创建标志为O_CLOEXEC,O_CREAT,O_DIRECTORY,O_EXCL,O_NOCTTY,O_NOFOLLOW,O_TMPFILE和O_TRUNC。文件状态标志是下面列出的所有其余标志。这两组标志之间的区别在于,文件创建标志会影响打开操作本身的语义,而文件状态标志会影响后续I / O操作的语义。可以检索和修改文件状态标志(在某些情况下);有关详细信息,请参见fcntl(2)。
文件创建标志和文件状态标志的完整列表如下:
- O_APPEND
- 该文件以追加模式打开。在每次write(2)之前,文件偏移都位于文件的末尾,就像使用lseek(2)一样。文件偏移量的修改和写操作作为单个原子步骤执行。
- 如果有多个进程一次将数据附加到文件,则O_APPEND可能会导致NFS文件系统上的文件损坏。这是因为NFS不支持附加到文件,因此客户端内核必须对其进行仿真,而没有竞争条件则无法完成。
- O_ASYNC
- 启用信号驱动的I / O:当可以在此文件描述符上进行输入或输出时,生成信号(默认情况下为SIGIO,但可以通过fcntl(2)进行更改)。此功能仅适用于终端,伪终端,套接字以及(自Linux 2.6起)管道和FIFO。有关更多详细信息,请参见fcntl(2)。另请参见下面的错误。
- O_CLOEXEC(since Linux 2.6.23)
- 启用新文件描述符的执行时关闭标志。指定此标志使程序可以避免其他fcntl(2)F_SETFD操作来设置FD_CLOEXEC标志。
- 请注意,在某些多线程程序中,此标志的使用必不可少,因为使用单独的fcntl(2)F_SETFD操作设置FD_CLOEXEC标志不足以避免争用情况,即一个线程打开文件描述符并尝试设置其close-使用fcntl(2)的on-exec标志与另一个线程同时执行fork(2)和execve(2)。根据执行顺序的不同,竞争可能导致open()返回的文件描述符无意间泄漏给fork(2)创建的子进程执行的程序。 (这种竞争原则上对于创建文件描述符的任何系统调用都是可行的,该文件描述符的close-on-exec标志应被设置,并且其他各种Linux系统调用也提供等效的O_CLOEXEC标志来解决此问题。)
- O_CREAT
- 如果路径名不存在,则将其创建为常规文件。
- 新文件的所有者(用户ID)设置为进程的有效用户ID。
- 新文件的组所有权(组ID)设置为进程的有效组ID(System V语义)或父目录的组ID(BSD语义)。在Linux上,行为取决于是否在父目录上设置了set-group-ID模式位:如果设置了该位,则将应用BSD语义;否则,将使用BSD语义。否则,将应用System V语义。对于某些文件系统,其行为还取决于mount(8)中描述的bsdgroups和sysvgroups挂载选项。
- mode参数指定在创建新文件时要应用的文件模式位。如果在标志中既未指定O_CREAT也未指定O_TMPFILE,则将忽略mode(因此可以将其指定为0,或简单地将其省略)。如果在标志中指定了O_CREAT或O_TMPFILE,则必须提供mode参数。如果未提供,则来自堆栈的一些任意字节将用作文件模式。
- 有效模式由进程的umask以通常的方式修改:在没有默认ACL的情况下,创建的文件的模式为(mode&tiumask)。
- 请注意,模式仅适用于将来对新创建的文件的访问。创建一个只读文件的open()调用很可能会返回一个读/写文件描述符。
- The following symbolic constants are provided for
mode:
- S_IRWXU
- 00700用户(文件所有者)具有读取,写入和执行权限
- S_IRUSR
- 00400用户具有读取权限
- S_IWUSR
- 00200用户具有写权限
- S_IXUSR
- 00100用户具有执行权限
- S_IRWXG
- 00070组具有读取,写入和执行权限
- S_IRGRP
- 00040组具有阅读权限
- S_IWGRP
- 00020组具有写权限
- S_IXGRP
- 00010组具有执行权限
- S_IRWXO
- 00007其他人具有读,写和执行权限
- S_IROTH
- 00004其他人具有阅读权限
- S_IWOTH
- 00002其他人有写许可
- S_IXOTH
- 00001个其他人具有执行权限
- According to POSIX, the effect when other bits are set in
mode
is unspecified.
On Linux, the following bits are also honored in
mode: - O_DIRECT(since Linux 2.4.10)
- 尝试最小化此文件之间的I / O的缓存影响。通常,这会降低性能,但是在特殊情况下(例如,应用程序自行缓存时)很有用。文件I / O是直接在用户空间缓冲区中进行的。 O_DIRECT标志独自致力于同步传输数据,但不能保证O_SYNC标志可以传输数据和必要的元数据。为了保证同步I / O,除O_DIRECT外还必须使用O_SYNC。有关更多讨论,请参见下面的注释。
- raw(8)中描述了块设备的语义相似(但已弃用)的接口。
- O_DIRECTORY
- 如果路径名不是目录,则导致打开失败。在内核版本2.1.126中添加了此标志,以避免在FIFO或磁带设备上调用opendir(3)时出现拒绝服务的问题。
- O_DSYNC
- 根据同步I / O数据完整性完成的要求,将完成对文件的写操作。
- 到write(2)(和类似的)返回时,输出数据以及检索该数据所需的任何文件元数据都已传输到底层硬件(即,好像每个write(2)之后是调用fdatasync(2))。请参阅下面的注释。
- O_EXCL
- 确保此调用创建了文件:如果将此标志与O_CREAT一起指定,并且路径名已经存在,则open()失败,错误为EEXIST。
- 当指定这两个标志时,将不遵循符号链接:如果pathname是符号链接,则无论符号链接指向何处,open()都会失败。
- 通常,如果不使用O_CREAT,则O_EXCL的行为是不确定的。有一个例外:在Linux 2.6和更高版本上,如果路径名引用的是块设备,则可以在不使用O_CREAT的情况下使用O_EXCL。如果系统正在使用该块设备(例如已安装),则open()失败,错误为EBUSY。
- 在NFS上,仅在内核2.6或更高版本上使用NFSv3或更高版本时才支持O_EXCL。在不提供O_EXCL支持的NFS环境中,依赖它执行锁定任务的程序将包含竞争条件。想要使用锁定文件执行原子文件锁定并且需要避免依赖NFS对O_EXCL的支持的可移植程序可以在同一文件系统上创建唯一文件(例如,合并主机名和PID),并使用link(2)进行创建锁定文件的链接。如果link(2)返回0,则锁定成功。否则,在唯一文件上使用stat(2)来检查其链接计数是否已增加到2,在这种情况下,锁定也成功。
- O_LARGEFILE
- (LFS)允许打开大小不能用off_t表示(但可以用off64_t表示)的文件。为了获得此定义,必须定义_LARGEFILE64_SOURCE宏(在包含任何头文件之前)。将_FILE_OFFSET_BITS功能测试宏设置为64(而不是使用O_LARGEFILE)是在32位系统上访问大文件的首选方法(请参见feature_test_macros(7))。
- O_NOATIME(since Linux 2.6.8)
- 读取文件(2)时,请勿更新文件的上次访问时间(inode中的st_atime)。
- This flag can be employed only if one of the following conditions is true:
- *
- 进程的有效UID与文件的所有者UID匹配。
- *
- 调用过程在其用户名称空间中具有CAP_FOWNER功能,并且文件的所有者UID在名称空间中具有映射。
- 该标志供索引或备份程序使用,在此情况下使用该标志可以显着减少磁盘活动量。该标志可能并非在所有文件系统上都有效。一个示例是NFS,服务器在其中维护访问时间。
- O_NOCTTY
- 如果路径名指向终端设备-参见tty(4)-即使该进程没有一个终端设备,它也不会成为该进程的控制终端。
- O_NOFOLLOW
- 如果路径名的结尾部分(即基本名)是符号链接,则打开失败,错误为ELOOP。路径名的较早部分中的符号链接仍将遵循。 (请注意,在这种情况下可能发生的ELOOP错误与打开失败的情况是无法区分的,因为在解析路径名的前缀部分中的组件时发现了太多的符号链接。)
- 该标志是FreeBSD扩展,已在2.1.126版中添加到Linux,随后在POSIX.1-2008中进行了标准化。
- 另请参见下面的O_PATH。
- O_NONBLOCKor O_NDELAY
- 如果可能,以非阻止模式打开文件。返回的文件描述符上的open()或任何后续I / O操作都不会导致调用过程等待。
- 请注意,此标志的设置对poll(2),select(2),epoll(7)等操作没有影响,因为这些接口仅通知调用方文件描述符是否为"就绪",这意味着清除O_NONBLOCK标志的文件描述符上执行的I / O操作不会阻塞。
- 请注意,此标志对常规文件和块设备无效;也就是说,无论是否设置了O_NONBLOCK,当需要进行设备活动时,I / O操作都会(简短地)阻塞。由于最终可能会实现O_NONBLOCK语义,因此在为常规文件和块设备指定此标志时,应用程序不应依赖于阻止行为。
- 有关fifo(命名管道)的处理,另请参见fifo(7)。有关O_NONBLOCK与强制性文件锁和文件租约一起使用的影响的讨论,请参见fcntl(2)。
- O_PATH(since Linux 2.6.39)
- 获取可用于两个目的的文件描述符:指示文件系统树中的位置,并执行纯粹在文件描述符级别起作用的操作。文件本身未打开,并且其他文件操作(例如,read(2),write(2),fchmod(2),fchown(2),fgetxattr(2),ioctl(2),mmap(2))失败错误EBADF。
- The following operations
can
be performed on the resulting file descriptor:
- *
- 关闭(2)。
- *
- fchdir(2),如果文件描述符引用目录(自Linux 3.5起)。
- *
- fstat(2)(从Linux 3.6开始)。
- *
- fstatfs(2)(从Linux 3.12开始)。
- *
- 复制文件描述符(dup(2),fcntl(2)F_DUPFD等)。
- *
- 获取和设置文件描述符标志(fcntl(2)F_GETFD和F_SETFD)。
- *
- 使用fcntl(2)F_GETFL操作检索打开的文件状态标志:返回的标志将包含O_PATH位。
- *
- 将文件描述符作为openat()和其他" * at()"系统调用的dirfd参数传递。这包括具有AT_EMPTY_PATH的linkat(2)(或通过使用AT_SYMLINK_FOLLOW的procfs),即使该文件不是目录也是如此。
- *
- 通过UNIX域套接字将文件描述符传递给另一个进程(请参见unix(7)中的SCM_RIGHTS)。
- 在标志中指定O_PATH时,将忽略O_CLOEXEC,O_DIRECTORY和O_NOFOLLOW以外的标志位。
- 使用O_PATH标志打开文件或目录不需要对象本身的权限(但是需要路径前缀中的目录具有执行权限)。取决于随后的操作,可以执行对适当文件权限的检查(例如,fchdir(2)要求对其文件描述符参数所引用的目录具有执行权限)。相反,通过使用O_RDONLY标志打开对文件系统对象的引用来获取对该文件系统对象的引用,要求调用者对该对象具有读取权限,即使随后的操作(例如fchdir(2),fstat(2))不需要读取也是如此对对象的权限。
- 如果路径名是符号链接,并且还指定了O_NOFOLLOW标志,则该调用将返回引用该符号链接的文件描述符。该文件描述符可以用作对fchownat(2),fstatat(2),linkat(2)和readlinkat(2)的调用,且其路径名为空,以使调用在符号链接上进行。
- 如果路径名引用尚未触发的自动挂载点,则没有其他文件系统挂载,则调用将返回引用自动挂载目录的文件描述符,而不会触发挂载。然后可以使用fstatfs(2)确定它实际上是否是未触发的自动安装点(.f_type == AUTOFS_SUPER_MAGIC)。
- O_PATH用于常规文件的一种用途是提供与POSIX.1相同的O_EXEC功能。这允许我们打开一个我们具有执行权限但没有读取权限的文件,然后执行该文件,并执行以下步骤:
char buf[PATH_MAX]; fd = open("some_prog", O_PATH); snprintf(buf, PATH_MAX, "/proc/self/fd/%d", fd); execl(buf, "some_prog", (char *) NULL);
- 还可以将O_PATH文件描述符作为fexecve(3)的参数传递。
- O_SYNC
- 将根据同步I / O文件完整性完成的要求完成对文件的写操作(与O_DSYNC提供的同步I / O数据完整性完成相反)。
- 到write(2)(或类似的)返回时,输出数据和关联的文件元数据已传输到底层硬件(即,就像每次write(2)之后都调用了fsync(2)一样)。请参阅下面的注释。
- O_TMPFILE(since Linux 3.11)
- 创建一个未命名的临时常规文件。 pathname参数指定目录;一个未命名的inode将在该目录的文件系统中创建。关闭最后一个文件描述符时,写入结果文件的所有内容都会丢失,除非为文件指定了名称。
- O_TMPFILE必须使用O_RDWR或O_WRONLY之一(也可以是O_EXCL)指定。如果未指定O_EXCL,则可以使用linkat(2)将临时文件链接到文件系统中,使其成为永久文件,并使用如下代码:
char path[PATH_MAX]; fd = open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); /* File I/O on 'fd'... */ linkat(fd, NULL, AT_FDCWD, "/path/for/file", AT_EMPTY_PATH); /* If the caller doesn't have the CAP_DAC_READ_SEARCH capability (needed to use AT_EMPTY_PATH with linkat(2)), and there is a proc(5) filesystem mounted, then the linkat(2) call above can be replaced with: snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd); linkat(AT_FDCWD, path, AT_FDCWD, "/path/for/file", AT_SYMLINK_FOLLOW); */
- 在这种情况下,与O_CREAT一样,open()模式参数确定文件许可模式。
- 与O_TMPFILE一起指定O_EXCL可以防止临时文件以上述方式链接到文件系统中。 (请注意,在这种情况下,O_EXCL的含义与其他情况下的O_EXCL的含义不同。)
- There are two main use cases for
O_TMPFILE:
- O_TMPFILE需要底层文件系统的支持;仅Linux文件系统的一个子集提供了该支持。在最初的实现中,在ext2,ext3,ext4,UDF,Minix和shmem文件系统中提供了支持。随后添加了对其他文件系统的支持,如下所示:XFS(Linux 3.15); Btrfs(Linux 3.16); F2FS(Linux 3.16);和ubifs(Linux 4.9)
- O_TRUNC
- 如果文件已经存在并且是常规文件,并且访问模式允许写入(即O_RDWR或O_WRONLY),它将被截断为长度0。如果文件是FIFO或终端设备文件,则O_TRUNC标志将被忽略。否则,未指定O_TRUNC的效果。
creat()
调用creat()等同于调用带有等于O_CREAT | O_WRONLY | O_TRUNC的标志的open()。
openat()
除了此处描述的差异外,openat()系统调用的操作与open()完全相同。
如果在路径名中给定的路径名是相对的,则它相对于文件描述符dirfd所引用的目录进行解释(而不是相对于调用进程的当前工作目录,如open()对相对路径名所做的那样) 。
如果路径名是相对的并且dirfd是特殊值AT_FDCWD,则路径名将相对于调用进程的当前工作目录(如open())进行解释。
如果路径名是绝对的,则dirfd被忽略。
openat2(2)
openat2(2)系统调用是openat()的扩展,并且提供了openat()功能的超集。它在openat2(2)中单独记录。
返回值
open(),openat()和creat()返回新文件描述符(非负整数),如果发生错误则返回-1(在这种情况下,将errno进行适当设置)。
错误说明
open(),openat()和creat()可能因以下错误而失败:
- EACCES
- 不允许请求对文件的访问,或者对路径名的路径前缀中的目录之一的搜索权限被拒绝,或者文件尚不存在,并且不允许对父目录的写访问。 (另请参见path_resolution(7)。)
- EACCES
- 在指定O_CREAT的情况下,启用了protected_fifos或protected_regular sysctl,该文件已经存在,并且是FIFO或常规文件,该文件的所有者既不是当前用户也不是包含目录的所有者,并且包含目录都是world -或组可写且具有粘性。有关详细信息,请参见proc(5)中对/ proc / sys / fs / protected_fifos和/ proc / sys / fs / protected_regular的描述。
- EDQUOT
- 在指定O_CREAT的情况下,该文件不存在,并且用户对文件系统上的磁盘块或索引节点的配额已用尽。
- EEXIST
- 路径名已经存在,并且使用了O_CREAT和O_EXCL。
- EFAULT
- 路径名指向您可访问的地址空间之外。
- EFBIG
- 请参阅溢出。
- EINTR
- 在阻塞等待完成打开慢速设备(例如fifo;请参阅fifo(7))的过程中,该调用被信号处理程序中断;参见signal(7)。
- EINVAL
- 文件系统不支持O_DIRECT标志。有关更多信息,请参见注释。
- EINVAL
- 标志中的值无效。
- EINVAL
- 在标志中指定了O_TMPFILE,但未指定O_WRONLY和O_RDWR。
- EINVAL
- 在标记中指定了O_CREAT,并且新文件路径名的最后部分("基本名")无效(例如,它包含基础文件系统不允许的字符)。
- EINVAL
- 路径名的最后一个组成部分("基本名称")无效(例如,它包含基础文件系统不允许的字符)。
- EISDIR
- 路径名是指目录,并且所请求的访问涉及写入(即,设置了O_WRONLY或O_RDWR)。
- EISDIR
- 路径名是指现有目录,在标志中指定了O_TMPFILE和O_WRONLY或O_RDWR中的一个,但是此内核版本不提供O_TMPFILE功能。
- ELOOP
- 解析路径名时遇到太多符号链接。
- ELOOP
- 路径名是符号链接,并且标记指定为O_NOFOLLOW,但不是O_PATH。
- EMFILE
- 已达到打开文件描述符数量的每个进程限制(请参阅getrlimit(2)中RLIMIT_NOFILE的描述)。
- ENAMETOOLONG
- 路径名太长。
- ENFILE
- 已达到系统范围内打开文件总数的限制。
- ENODEV
- 路径名是设备专用文件,不存在对应的设备。 (这是一个Linux内核错误;在这种情况下,必须返回ENXIO。)
- ENOENT
- 未设置O_CREAT,并且命名文件不存在。
- ENOENT
- 路径名中的目录组件不存在或为悬挂的符号链接。
- ENOENT
- 路径名是指不存在的目录,在标志中指定了O_TMPFILE和O_WRONLY或O_RDWR中的一个,但是此内核版本不提供O_TMPFILE功能。
- ENOMEM
- 命名文件是FIFO,但是无法分配FIFO缓冲区的内存,因为已经达到了每个用户对管道的内存分配的硬限制,并且调用者没有特权。参见pipe(7)。
- ENOMEM
- 内核内存不足。
- ENOSPC
- 要创建路径名,但是包含路径名的设备没有空间容纳新文件。
- ENOTDIR
- 实际上,在路径名中用作目录的组件不是目录,或者未指定O_DIRECTORY且路径名不是目录。
- ENXIO
- O_NONBLOCK |设置为O_WRONLY,命名文件为FIFO,并且没有进程打开FIFO以供读取。
- ENXIO
- 该文件是设备专用文件,不存在相应的设备。
- ENXIO
- 该文件是UNIX域套接字。
- EOPNOTSUPP
- 包含路径名的文件系统不支持O_TMPFILE。
- EOVERFLOW
- 路径名是指太大而无法打开的常规文件。通常的情况是,在没有-D_FILE_OFFSET_BITS = 64的32位平台上编译的应用程序试图打开大小超过(1
- EPERM
- 指定了O_NOATIME标志,但是调用者的有效用户ID与文件的所有者不匹配,并且调用者没有特权。
- EPERM
- 该操作被文件封条阻止;参见fcntl(2)。
- EROFS
- 路径名是指只读文件系统上的文件,并且请求了写访问权。
- ETXTBSY
- 路径名是指当前正在执行的可执行映像,并且已请求写访问权限。
- ETXTBSY
- 路径名是指当前用作交换文件的文件,并且已指定O_TRUNC标志。
- ETXTBSY
- 路径名是指内核当前正在读取的文件(例如,用于模块/固件加载),并且请求了写访问权限。
- EWOULDBLOCK
- 指定了O_NONBLOCK标志,并且对该文件保留了不兼容的租约(请参见fcntl(2))。
openat()可能会发生以下其他错误:
- EBADF
- dirfd不是有效的文件描述符。
- ENOTDIR
- pathname是相对路径名,dirfd是引用目录以外的文件的文件描述符。
版本
openat()在内核2.6.16中添加到Linux;库支持已添加到版本2.4中的glibc。
遵循规范
open(),creat()SVr4、4.3BSD,POSIX.1-2001,POSIX.1-2008。
openat():POSIX.1-2008。
openat2(2)是特定于Linux的。
O_DIRECT,O_NOATIME,O_PATH和O_TMPFILE标志是特定于Linux的。必须定义_GNU_SOURCE以获得其定义。
O_CLOEXEC,O_DIRECTORY和O_NOFOLLOW标志在POSIX.1-2001中未指定,但在POSIX.1-2008中指定。从glibc 2.12开始,可以通过定义值大于或等于200809L的_POSIX_C_SOURCE或值大于或等于700的_XOPEN_SOURCE来获得其定义。在glibc 2.11和更早版本中,可以通过定义_GNU_SOURCE获得定义。
如feature_test_macros(7)中所述,必须在包含任何头文件之前定义功能测试宏,例如_POSIX_C_SOURCE,_XOPEN_SOURCE和_GNU_SOURCE。
备注
在Linux下,有时需要打开O_NONBLOCK标志,但不一定要进行读写操作。例如,可以使用它来打开设备,以获得与ioctl(2)一起使用的文件描述符。
O_RDONLY的(未定义)效果| O_TRUNC在不同的实现中有所不同。在许多系统上,文件实际上被截断了。
请注意,open()可以打开设备专用文件,但是creat()不能创建它们。使用mknod(2)代替。
如果文件是新创建的,则将其st_atime,st_ctime,st_mtime字段(分别是上次访问时间,上次状态更改时间和上次修改时间;请参见stat(2))设置为当前时间,等等。父目录的st_ctime和st_mtime字段。否则,如果由于O_TRUNC标志而修改了文件,则将其st_ctime和st_mtime字段设置为当前时间。
/ proc / [pid] / fd目录中的文件显示带有PID pid的进程的打开文件描述符。 / proc / [pid] / fdinfo目录中的文件甚至显示有关这些文件描述符的更多信息。有关这两个目录的更多详细信息,请参见proc(5)。
Linux头文件没有定义O_ASYNC。而是定义了(源自BSD的)FASYNC同义词。
Open file descriptions
术语"打开文件描述"是POSIX用来指代系统范围的打开文件表中的条目的术语。在其他上下文中,该对象也被称为"打开文件对象","文件句柄","打开文件表项",或者-在内核开发人员看来-结构文件。
复制文件描述符时(使用dup(2)或类似方法),副本文件将引用与原始文件描述符相同的打开文件描述,因此这两个文件描述符共享文件偏移量和文件状态标志。这种共享也可能在进程之间发生:通过fork(2)创建的子进程继承其父级文件描述符的重复项,并且这些重复项引用相同的打开文件描述。
文件的每个open()都会创建一个新的打开文件描述;因此,可能有多个与文件inode对应的打开文件描述。
在Linux上,可以使用kcmp(2)KCMP_FILE操作来测试两个文件描述符(在同一进程中还是在两个不同进程中)是否引用相同的打开文件描述。
Synchronized I/O
POSIX.1-2008"同步I / O"选项指定同步I / O的不同变体,并指定用于控制行为的open()标志O_SYNC,O_DSYNC和O_RSYNC。无论实现是否支持此选项,它都必须至少支持对常规文件使用O_SYNC。
Linux实现O_SYNC和O_DSYNC,但不实现O_RSYNC。某种程度上来说,glibc定义O_RSYNC具有与O_SYNC相同的值。 (O_RSYNC是在HP PA-RISC的Linux头文件中定义的,但未使用。)
O_SYNC提供同步的I / O文件完整性完成,这意味着写入操作会将数据和所有相关的元数据刷新到底层硬件。 O_DSYNC提供同步的I / O数据完整性完成,这意味着写操作会将数据刷新到底层硬件,但仅刷新允许后续读取操作成功完成所需的元数据更新。数据完整性完成可以减少不需要保证文件完整性完成的应用程序所需的磁盘操作数。
要了解两种完成类型之间的区别,请考虑两部分文件元数据:文件上次修改时间戳(st_mtime)和文件长度。所有写操作都会更新最后的文件修改时间戳,但是只有将数据添加到文件末尾的写操作才会更改文件长度。不需要最后修改时间戳即可确保读取成功完成,但是文件长度可以。因此,O_DSYNC将仅保证刷新对文件长度元数据的更新(而O_SYNC也将始终刷新最后的修改时间戳记元数据)。
在Linux 2.6.33之前,Linux仅为open()实现O_SYNC标志。但是,当指定了该标志时,大多数文件系统实际上提供了与同步I / O数据完整性完成等效的功能(即,实际上将O_SYNC实现为与O_DSYNC等效的功能)。
从Linux 2.6.33开始,提供了适当的O_SYNC支持。但是,为了确保向后二进制兼容性,将O_DSYNC定义为与历史O_SYNC相同的值,并且将O_SYNC定义为包括O_DSYNC标志值的新(两位)标志值。这样可以确保针对新标头编译的应用程序在2.6.33之前的内核上至少具有O_DSYNC语义。
C library/kernel differences
从2.26版开始,用于open()的glibc包装函数将使用openat()系统调用,而不是内核的open()系统调用。对于某些体系结构,在2.26之前的glibc版本中也是如此。
NFS
NFS底层的协议存在很多不足之处,其中包括O_SYNC和O_NDELAY。
在启用了UID映射的NFS文件系统上,open()可能返回文件描述符,但是,例如,EACCES拒绝了read(2)请求。这是因为客户端通过检查权限来执行open(),但是服务器会根据读写请求执行UID映射。
FIFOs
打开FIFO的读取或写入端会阻塞,直到另一端也被打开(由另一个进程或线程)。有关更多详细信息,请参见fifo(7)。
File access mode
与标志中可以指定的其他值不同,访问模式值O_RDONLY,O_WRONLY和O_RDWR不指定单个位。而是,它们定义了标志的低两位,分别定义为0、1和2。换句话说,组合O_RDONLY | O_WRONLY是一个逻辑错误,并且当然没有与O_RDWR相同的含义。
Linux在标志中保留特殊的非标准访问模式3(二进制11),其含义是:检查文件的读写许可权,并返回不能用于读取或写入的文件描述符。一些Linux驱动程序使用此非标准访问模式来返回文件描述符,该文件描述符仅用于特定于设备的ioctl(2)操作。
Rationale for openat() and other directory file descriptor APIs
使用目录文件描述符参数的openat()以及其他系统调用和库函数(即execveat(2),faccessat(2),fanotify_mark(2),fchmodat(2),fchownat(2),fspick(2)) ,fstatat(2),futimesat(2),linkat(2),mkdirat(2),move_mount(2),mknodat(2),name_to_handle_at(2),open_tree(2),openat2(2),readlinkat(2) ,renameat(2),statx(2),symlinkat(2),unlinkat(2),utimensat(2),mkfifoat(3)和scandirat(3))解决了它们之前的旧接口的两个问题。在此,以openat()调用为例进行解释,但是其原理与其他接口类似。
首先,openat()允许应用程序避免在使用open()打开当前工作目录以外的目录中的文件时可能发生的竞争状况。这些竞争条件是由于以下事实导致的:给open()的目录前缀的某些组件可以与对open()的调用并行更改。例如,假设如果文件dir1 / dir2 / xxx存在,我们希望创建文件dir1 / dir2 / xxx.dep。问题在于,在存在检查和文件创建步骤之间,可以修改dir1或dir2(可能是符号链接)以指向其他位置。可以通过打开目标目录的文件描述符,然后将该文件描述符指定为fstatat(2)和openat()的dirfd参数来避免这种竞争。使用dirfd文件描述符还具有其他好处:
- *
- 即使目录已重命名,文件描述符也是对目录的稳定引用;和
- *
- 打开文件描述符可防止卸载基础文件系统,就像进程在文件系统上具有当前工作目录时一样。
其次,openat()允许通过应用程序维护的文件描述符来实现每个线程的"当前工作目录"。 (也可以通过使用/ proc / self / fd / dirfd的技巧来获得此功能,但是效率较低。)
这些API的dirfd参数可以通过使用open()或openat()打开目录(带有O_RDONLY或O_PATH标志)来获取。或者,可以通过将dirfd(3)应用于使用opendir(3)创建的目录流来获得这种文件描述符。
当为这些API提供AT_FDCWD的dirfd参数或指定的路径名是绝对路径时,它们将以与相应常规API相同的方式处理其pathname参数。但是,在这种情况下,一些API具有flags参数,该参数提供对相应常规API所不具备的功能的访问。
O_DIRECT
O_DIRECT标志可能对用户空间缓冲区的长度和地址以及I / O的文件偏移量施加对齐限制。在Linux中,对齐限制因文件系统和内核版本而异,并且可能完全不存在。但是,当前没有应用程序独立于文件系统的接口来发现给定文件或文件系统的这些限制。某些文件系统为此提供了自己的接口,例如xfsctl(3)中的XFS_IOC_DIOINFO操作。
在Linux 2.4下,传输大小以及用户缓冲区和文件偏移量的对齐都必须是文件系统逻辑块大小的倍数。从Linux 2.6.0开始,与基础存储的逻辑块大小(通常为512字节)对齐即可。逻辑块大小可以使用ioctl(2)BLKSSZGET操作确定,也可以使用以下命令从Shell中确定:
blockdev --getss
如果内存缓冲区是私有映射(即,使用mmap(2)MAP_PRIVATE标志创建的任何映射,则此O_DIRECT I / O都不应与fork(2)系统调用同时运行);这包括在堆上分配的内存和静态地分配的缓冲区)。任何此类I / O(无论是通过异步I / O接口提交还是从进程中的其他线程提交)都应在调用fork(2)之前完成。否则可能会导致数据损坏以及父子进程中未定义的行为。当使用带有MAP_SHARED标志的shmat(2)或mmap(2)创建O_DIRECT I / O的内存缓冲区时,此限制不适用。当已将内存缓冲区建议为madvise(2)的MADV_DONTFORK时,此限制也将不适用,以确保在fork(2)之后子级将无法使用该缓冲区。
O_DIRECT标志是在SGI IRIX中引入的,它具有与Linux 2.4相似的对齐限制。 IRIX还具有fcntl(2)调用以查询适当的对齐方式和大小。 FreeBSD 4.x引入了一个相同名称的标志,但是没有对齐限制。
在Linux 2.4.10版本中添加了O_DIRECT支持。较早的Linux内核仅忽略此标志。某些文件系统可能未实现该标志,在这种情况下,如果使用open(),则错误EINVAL会失败。
应用程序应避免将O_DIRECT和普通I / O混合到同一文件,尤其是同一文件中重叠的字节区域。即使在这种情况下文件系统正确处理了一致性问题,总体I / O吞吐量也可能比单独使用任何一种模式都要慢。同样,应用程序应避免将具有直接I / O的mmap(2)文件混合到同一文件中。
具有NFS的O_DIRECT的行为将与本地文件系统不同。较旧的内核或以某些方式配置的内核可能不支持此组合。 NFS协议不支持将标志传递给服务器,因此O_DIRECT I / O将仅绕过客户机上的页面缓存。服务器可能仍会缓存I / O。客户端要求服务器使I / O同步,以保留O_DIRECT的同步语义。在这种情况下,某些服务器的性能会很差,尤其是在I / O大小较小的情况下。某些服务器可能还被配置为向客户机说谎有关I / O已达到稳定存储的情况。这样可以避免由于服务器电源故障而对数据完整性造成一定风险的性能损失。 Linux NFS客户端对O_DIRECT I / O没有对齐限制。
总之,O_DIRECT是潜在强大的工具,应谨慎使用。建议应用程序将使用O_DIRECT作为性能选项,默认情况下禁用。
BUGS
当前,在调用open()时无法通过指定O_ASYNC来启用信号驱动的I / O。使用fcntl(2)启用此标志。
在尝试确定内核是否支持O_TMPFILE功能时,必须检查两种不同的错误代码EISDIR和ENOENT。
当在标志中同时指定O_CREAT和O_DIRECTORY并且路径名指定的文件不存在时,open()将创建一个常规文件(即O_DIRECTORY被忽略)。
另外参见
chmod(2),chown(2),close(2),dup(2),fcntl(2),link(2),lseek(2),mknod(2),mmap(2),mount(2), open_by_handle_at(2),openat2(2),read(2),socket(2),stat(2),umask(2),unlink(2),write(2),fopen(3),acl(5), fifo(7),inode(7),path_resolution(7),symlink(7)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。