资讯详情

glibc 知:手册13:底层输入/输出

文章目录

  • 1. 前言
  • 2. 底层输入/输出
    • 2.1. 打开和关闭文件
    • 2.2. 输入输出原语
    • 2.3. 设置描述符的文件位置
    • 2.4. 描述符和流
    • 2.5. 混合流和描述符的危险
      • 2.5.1. 链接通道
      • 2.5.2. 独立通道
      • 2.5.3. 清洗流
    • 2.6. 快速分散-聚集 I/O
    • 2.7. 复制两个文件之间的数据
    • 2.8. 内存映射I/O
    • 2.9. 等待输入或输出
    • 2.10. 同步 I/O 操作
    • 2.11. 并行执行 I/O 操作
      • 2.11.1. 异步读写操作
      • 2.11.2. 获取 AIO 操作状态
      • 2.11.3. 进入一致状态
      • 2.11.4. 取消 AIO 操作
      • 2.11.5. 如何优化AIO实现
    • 2.12. 控制文件操作
    • 2.13. 复制描述符
    • 2.14. 文件描述符号
    • 2.15. 文件状态标志
      • 2.15.1. 文件访问模式
      • 2.15.2. 开放时间标志
      • 2.15.3. I/O 操作模式
      • 2.15.4. 获取和设置文件状态标志
    • 2.16. 文件
    • 2.17. 打开文件描述锁
    • 2.18. 打开文件描述锁示例
    • 2.19. 中断驱动输入
    • 2.20. 通用 I/O 控制操作
  • 3. 参考

1. 前言

The GNU C Library Reference Manual for version 2.35

2. 底层输入/输出

Low-Level Input/Output

本章描述了文件描述符执行底层输入/输出操作的函数。这些函数包括输入/输出流中描述的高级函数 I/O 函数的原语和用于执行流上无等效项的底层控制操作的函数。

流级 I/O 更灵活,通常更方便;因此,程序员通常只在必要时使用描述符级别的函数。这些都是常见的原因:

  • 读取大块二进制文件。
  • 在分析之前,将整个文件读入核心。
  • 通过描述符执行数据传输以外的操作。(您可以使用它 fileno 获取与流量对应的描述符。
  • 将描述符传递给子过程。(儿童可以创建自己的流动来使用其继承的描述符,但不能直接继承。

2.1. 打开和关闭文件

Opening and Closing Files

本节介绍使用文件描述符打开和关闭文件的原语。open 和 creat 函数在头文件 fcntl.h 中声明,而 close 在 unistd.h 中声明。

函数:int open (const char *filename, int flags[, mode_t mode])

Preliminary: | MT-Safe | AS-Safe | AC-Safe fd | See POSIX Safety Concepts.

open 函数为由 filename 创建命名文件并返回新的文件描述符。最初,文件的文件位置指示符位于文件的开头。参数模式(请参考访问权限的模式位置)仅用于创建文件,但在任何情况下提供参数都没有坏处。

flags 如何打开参数控制文件。这是一个位置掩码;您可以通过适当的参数按位或创建值(使用) C 中的“|操作符)。有关可用参数,请参阅文件状态标志。

open 正常返回值是非负整数文件的描述符。如果出现错误,将改为返回值 -1.除了通常的文件名错误(请参考文件名错误)外,函数还定义了以下功能 errno 错误条件:

EACCES

文件存在,但按照 flags 参数要求不可读/可写,或文件不存在,目录不可写,无法创建。

EEXIST

O_CREAT 和 O_EXCL 已设置,并存在命名文件。

EINTR

打开操作被信号中断。请参考信号中断的原语。

EISDIR

flags 参数指定权限,文件为目录。

EMFILE

过程中打开的文件太多了。文件描述符的最大数量由文件描述符的最大数量组成 RLIMIT_NOFILE 资源限制控制;请参考资源使用限制。

ENFILE

目前,包含该目录的整个系统或文件系统不能支持任何其他打开的文件。(问题是 GNU/Hurd 不会发生在系统上。

ENOENT

指定文件不存在,也没有指定 O_CREAT。

ENOSPC

由于没有剩余的磁盘空间,包含新文件的目录或文件系统无法扩展。

ENXIO

O_NONBLOCK 和 O_WRONLY 都在 flags 设置参数,由 filename 命名文件是一个 FIFO(参见 Pipes 和 FIFOs),并且没有打开文件读取进程。

EROFS

该文件位于只读文件系统中 flags 设置在参数中 O_WRONLY、O_RDWR 和 O_TRUNC 其中任何一个,或 O_CREAT 而且文件不存在。

如果在 32 使用位机 _FILE_OFFSET_BITS == 64 翻译源文件,函数 open 返回大文件模式打开的文件描述符,使文件处理函数最大限度地使用 2^63 字节和偏移量为 -2^63 到 2^63.这对用户来说是透明的,因为所有的低级文件处理功能都被同样的替换。

这个函数是多线程序中的取消点。如果调用线程 open 当某些资源被分配(如内存、文件描述符、信号量或其他)时,就会出现问题。如果线程被取消,这些资源将保持分配,直到程序结束。为避免这种情况,应使用取消程序保护 open 的调用。

open 函数是创建流的 fopen 和 freopen 函数底层原语。

函数:int open64 (const char *filename, int flags[, mode_t mode])

Preliminary: | MT-Safe | AS-Safe | AC-Safe fd | See POSIX Safety Concepts.

这个功能类似于打开。它返回文件描述符,用于访问文件名称命名的文件。唯一的区别是 32 位系统上,文件是以大文件模式打开的。即,文件长度和文件偏移量可以超过 31 位。

当使用 _FILE_OFFSET_BITS == 64 在翻译源代码时,这个函数实际上是名称 open 可用。也就是说,使用 64 新扩展位文件的大小和偏移量 API 透明地替换旧的 API。

过时函数:int creat (const char *filename, mode_t mode)

Preliminary: | MT-Safe | AS-Safe | AC-Safe fd | See POSIX Safety Concepts.

此功能已过时。

creat (filename, mode) 

相当于:

open (filename, O_WRONLY | O_CREAT | O_TRUNC, mode)

如果在 32 位机器上使用 _FILE_OFFSET_BITS == 64 转换源,则函数 creat 返回以大文件模式打开的文件描述符,这使文件处理函数能够使用最大为 2^63 且偏移量为 -2^ 的文件63 到 2^63。这对用户来说是透明的,因为所有低级文件处理功能都被同等替换了。

过时的函数:int creat64 (const char *filename, mode_t mode)

Preliminary: | MT-Safe | AS-Safe | AC-Safe fd | See POSIX Safety Concepts.

此功能类似于创建。它返回一个文件描述符,可用于访问由文件名命名的文件。唯一的区别是在 32 位系统上,文件是以大文件模式打开的。即,文件长度和文件偏移量可以超过 31 位。

要使用此文件描述符,不得使用正常操作,而是使用名为 *64 的对应操作,例如 read64。

当使用 _FILE_OFFSET_BITS == 64 翻译源代码时,此函数实际上在名称 open 下可用。即,使用 64 位文件大小和偏移量的新扩展 API 透明地替换了旧 API。

函数:int close (int filedes)

Preliminary: | MT-Safe | AS-Safe | AC-Safe fd | See POSIX Safety Concepts.

函数 close 关闭文件描述符文件。关闭文件会产生以下后果:

  • 文件描述符被释放。
  • 文件上进程拥有的任何记录锁都被解锁。
  • 当与管道或 FIFO 关联的所有文件描述符都已关闭时,任何未读数据都将被丢弃。

这个函数是多线程程序中的一个取消点。如果线程在调用 close 时分配了一些资源(如内存、文件描述符、信号量或其他),则会出现问题。如果线程被取消,这些资源将保持分配状态,直到程序结束。为避免这种情况,应使用取消处理程序保护对 close 的调用。

close 的正常返回值为 0;如果失败,则返回值 -1。为此函数定义了以下 errno 错误条件:

EBADF

filedes 参数不是有效的文件描述符。

EINTR

关闭通话被信号中断。请参阅被信号中断的原语。以下是如何正确处理 EINTR 的示例:

TEMP_FAILURE_RETRY (close (desc));

ENOSPC

EIO

EDQUOT

当 NFS 访问文件时,有时直到关闭才能检测到这些写入错误。有关其含义的详细信息,请参阅输入和输出原语。

请注意,没有单独的 close64 函数。这不是必需的,因为此函数不确定也不依赖于文件的模式。执行关闭操作的内核知道描述符用于哪种模式并且可以处理这种情况。

要关闭流,请调用 fclose(请参阅关闭流),而不是尝试使用 close 关闭其底层文件描述符。这会刷新任何缓冲的输出并更新流对象以指示它已关闭。

函数:int close_range (unsigned int lowfd, unsigned int maxfd, int flags)

Preliminary: | MT-Safe | AS-Safe | AC-Safe fd | See POSIX Safety Concepts.

函数 close_range 将文件描述符从 lowfd 关闭到 maxfd(包括)。此函数类似于根据标志在指定的文件描述符范围内调用 close。

此功能仅在最近的 Linux 版本上受支持,并且 GNU C 库不提供任何回退(应用程序将需要处理可能的 ENOSYS)。

标志添加了有关文件如何关闭的选项。Linux 目前支持:

CLOSE_RANGE_UNSHARE

在关闭文件描述符之前取消共享文件描述符表。

CLOSE_RANGE_CLOEXEC

设置 FD_CLOEXEC 位而不是关闭文件描述符。

close_range 的正常返回值为 0;如果失败,则返回值 -1。为此函数定义了以下 errno 错误条件:

EINVAL

lowfd 值大于 maxfd 或使用了不支持的标志。

ENOMEM

要么没有足够的内存用于操作,要么进程的地址空间不足。只有在使用 CLOSE_RANGE_UNSHARED 标志时才会发生这种情况。

EMFILE

该进程打开的文件太多,只有在使用 CLOSE_RANGE_UNSHARED 标志时才会发生。文件描述符的最大数量由 RLIMIT_NOFILE 资源限制控制;请参阅限制资源使用。

ENOSYS

内核没有实现所需的功能。

函数:void closefrom (int lowfd)

Preliminary: | MT-Safe | AS-Safe | AC-Safe fd | See POSIX Safety Concepts.

closefrom 函数关闭所有大于或等于 lowfd 的文件描述符。这个函数类似于为所有打开的文件描述符调用close,不小于lowfd。

已经关闭的文件描述符被忽略。

2.2. 输入和输出原语

Input and Output Primitives

本节介绍对文件描述符执行原始输入和输出操作的函数:read、write 和 lseek。这些函数在头文件 unistd.h 中声明。

数据类型:ssize_t

此数据类型用于表示可以在单个操作中读取或写入的块的大小。它类似于 size_t,但必须是有符号类型。

函数:ssize_t read (int fields, void *buffer, size_t size)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

read 函数从带有描述符的文件中读取最多 size 个字节,并将结果存储在缓冲区中。(这不一定是字符串,也没有添加终止空字符。)

返回值是实际读取的字节数。这可能小于大小;例如,如果文件中没有那么多字节,或者没有那么多字节立即可用。确切的行为取决于它是什么类型的文件。请注意,读取小于 size 字节不是错误。

零值表示文件结束(除非 size 参数的值也为零)。这不被视为错误。如果您在文件结束时继续调用 read,它将继续返回零并且什么也不做。

如果 read 返回至少一个字符,则无法判断是否已到达文件结尾。但是,如果您确实到达了末尾,则下一次读取将返回零。

如果发生错误,read 返回 -1。为此函数定义了以下 errno 错误条件:

EAGAIN

通常,当没有立即可用的输入时,read 会等待一些输入。但是如果为文件设置了 O_NONBLOCK 标志(参见文件状态标志),read 立即返回而不读取任何数据,并报告此错误。

兼容性说明:大多数 BSD Unix 版本为此使用不同的错误代码:EWOULDBLOCK。在 GNU C 库中,EWOULDBLOCK 是 EAGAIN 的别名,因此使用哪个名称并不重要。

在某些系统上,如果内核无法找到足够的物理内存来锁定用户的页面,则从字符特殊文件中读取大量数据也会因 EAGAIN 而失败。这仅限于通过直接内存访问传输到用户内存的设备,这意味着它不包括终端,因为它们总是在内核中使用单独的缓冲区。这个问题永远不会在 GNU/Hurd 系统上发生。

任何可能导致 EAGAIN 的条件都可能导致成功读取,该读取返回的字节数少于请求的字节数。立即再次调用 read 将导致 EAGAIN。

EBADF

filedes 参数不是有效的文件描述符,或者未打开以供读取。

EINTR

读取在等待输入时被信号中断。请参阅被信号中断的原语。信号不一定会导致 read 返回 EINTR;相反,它可能会导致成功读取,返回的字节数少于请求的字节数。

EIO

对于许多设备和磁盘文件,此错误代码表示硬件错误。

当后台进程尝试从控制终端读取数据时也会发生 EIO,并且通过向其发送 SIGTTIN 信号来停止进程的正常操作不起作用。如果信号被阻止或忽略,或者因为进程组是孤立的,则可能会发生这种情况。有关作业控制的更多信息,请参阅作业控制;有关信号的信息,请参阅信号处理。

EINVAL

在某些系统中,从字符或块设备读取时,位置和大小偏移必须与特定的块大小对齐。此错误表明偏移未正确对齐。

请注意,没有名为 read64 的函数。这不是必需的,因为此函数不会直接修改或处理可能的宽文件偏移量。由于内核在内部处理此状态,因此 read 函数可用于所有情况。

这个函数是多线程程序中的一个取消点。如果线程在调用 read 时分配了一些资源(如内存、文件描述符、信号量或其他),则会出现问题。如果线程被取消,这些资源将保持分配状态,直到程序结束。为避免这种情况,应使用取消处理程序保护对读取的调用。

read 函数是所有从流中读取的函数的底层原语,例如 fgetc。

函数:ssize_t pread (int fields, void *buffer, size_t size, off_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

预读函数类似于读函数。前三个参数相同,返回值和错误码也对应。

不同之处在于第四个参数及其处理。数据块不是从文件描述符文件的当前位置读取的。而是从位置偏移开始的文件中读取数据。文件描述符本身的位置不受操作的影响。该值与调用前相同。

当使用 _FILE_OFFSET_BITS == 64 编译源文件时,pread 函数实际上是 pread64 并且类型 off_t 有 64 位,这使得处理长达 2^63 字节的文件成为可能。

pread 的返回值描述了读取的字节数。在错误情况下,它像 read 一样返回 -1 并且错误代码也相同,但添加了以下内容:

EINVAL

给定的偏移值是负数,因此是非法的。

ESPIPE

文件描述符filedes 与管道或FIFO 相关联,并且该设备不允许定位文件指针。

该函数是 Unix 单一规范版本 2 中定义的扩展。

函数:ssize_t pread64 (int fields, void *buffer, size_t size, off64_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此功能类似于 pred 功能。不同之处在于 offset 参数的类型是 off64_t 而不是 off_t,这使得在 32 位机器上可以寻址大于 2^31 字节和最多 2^63 字节的文件。文件描述符文件必须使用 open64 打开,否则 off64_t 可能的大偏移量将导致小文件模式下的描述符错误。

当在 32 位机器上使用 _FILE_OFFSET_BITS == 64 编译源文件时,这个函数实际上可以在名称下使用 pread 并且因此透明地替换了 32 位接口。

函数:ssize_t write (int fields, const void *buffer, size_t size)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

write 函数将最多 size 个字节从缓冲区写入到具有描述符的文件中。缓冲区中的数据不一定是字符串,并且会像任何其他字符一样输出空字符。

返回值是实际写入的字节数。这可能是大小,但总是可以更小。您的程序应始终在循环中调用 write,迭代直到写入所有数据。

一旦写入返回,数据就会被排队写入并且可以立即读回,但不一定会立即写入永久存储。当您需要在继续之前确保您的数据已永久存储时,您可以使用 fsync。(系统批量连续写入并在方便时一次完成所有写入会更有效。通常它们总是会在一分钟或更短的时间内写入磁盘。)现代系统提供了另一个函数 fdatasync ,它只保证文件的完整性数据,因此速度更快。可以使用 O_FSYNC 开放模式,让 write 总是先把数据存到磁盘再返回;请参阅 I/O 操作模式。

如果发生错误,write 返回 -1。为此函数定义了以下 errno 错误条件:

EAGAIN

通常,写块直到写操作完成。但是如果为文件设置了 O_NONBLOCK 标志(请参阅文件上的控制操作),它会立即返回而不写入任何数据并报告此错误。可能导致进程阻塞输出的情况示例是写入支持流控制的终端设备,其中输出已因接收到 STOP 字符而暂停。

兼容性说明:大多数 BSD Unix 版本为此使用不同的错误代码:EWOULDBLOCK。在 GNU C 库中,EWOULDBLOCK 是 EAGAIN 的别名,因此使用哪个名称并不重要。

在某些系统上,如果内核无法找到足够的物理内存来锁定用户的页面,则从字符特殊文件写入大量数据也会因 EAGAIN 而失败。这仅限于通过直接内存访问传输到用户内存的设备,这意味着它不包括终端,因为它们总是在内核中使用单独的缓冲区。在 GNU/Hurd 系统上不会出现这个问题。

EBADF

filedes 参数不是有效的文件描述符,或者未打开以供写入。

EFBIG

文件的大小将变得比实现可以支持的更大。

EINTR

写入操作在等待完成时被信号中断。信号不一定会导致 write 返回 EINTR;相反,它可能会导致成功写入,写入的字节数少于请求的字节数。请参阅被信号中断的原语。

EIO

对于许多设备和磁盘文件,此错误代码表示硬件错误。

ENOSPC

包含该文件的设备已满。

EPIPE

当您尝试写入未打开以供任何进程读取的管道或 FIFO 时,将返回此错误。发生这种情况时,也会向进程发送一个 SIGPIPE 信号;请参阅信号处理。

EINVAL

在某些系统中,当写入字符或块设备时,位置和大小偏移必须与特定的块大小对齐。此错误表明偏移未正确对齐。

除非您已安排防止 EINTR 失败,否则应在每次失败的 write 调用后检查 errno,如果错误是 EINTR,则应简单地重复调用。请参阅被信号中断的原语。执行此操作的简单方法是使用宏 TEMP_FAILURE_RETRY,如下所示:

nbytes = TEMP_FAILURE_RETRY (write (desc, buffer, count));

请注意,没有名为 write64 的函数。这不是必需的,因为此函数不会直接修改或处理可能的宽文件偏移量。由于内核在内部处理此状态,因此 write 函数可用于所有情况。

这个函数是多线程程序中的一个取消点。如果线程在调用 write 时分配了一些资源(如内存、文件描述符、信号量或其他),则会出现问题。如果线程被取消,这些资源将保持分配状态,直到程序结束。为避免这种情况,应使用取消处理程序保护对 write 的调用。

write 函数是所有写入流的函数的底层原语,例如 fputc。

函数:ssize_t pwrite (int fields, const void *buffer, size_t size, off_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

pwrite 函数类似于 write 函数。前三个参数相同,返回值和错误码也对应。

不同之处在于第四个参数及其处理。数据块没有写入文件描述符filedes的当前位置。相反,数据从位置偏移开始写入文件。文件描述符本身的位置不受操作的影响。该值与调用前相同。

但是,在 Linux 上,如果使用 O_APPEND 打开文件,则 pwrite 会将数据附加到文件的末尾,而不管 offset 的值如何。

当使用 _FILE_OFFSET_BITS == 64 编译源文件时,pwrite 函数实际上是 pwrite64 并且类型 off_t 有 64 位,这使得处理长达 2^63 字节的文件成为可能。

pwrite 的返回值描述了写入的字节数。在错误情况下,它像 write 一样返回 -1 并且错误代码也相同,但添加了以下内容:

EINVAL

给定的偏移值是负数,因此是非法的。

ESPIPE

文件描述符filedes 与管道或FIFO 相关联,并且该设备不允许定位文件指针。

该函数是 Unix 单一规范版本 2 中定义的扩展。

函数:ssize_t pwrite64 (int fields, const void *buffer, size_t size, off64_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数类似于 pwrite 函数。不同之处在于 offset 参数的类型是 off64_t 而不是 off_t,这使得在 32 位机器上可以寻址大于 2^31 字节和最多 2^63 字节的文件。文件描述符文件必须使用 open64 打开,否则 off64_t 可能的大偏移量将导致小文件模式下的描述符错误。

当在 32 位机器上使用 _FILE_OFFSET_BITS == 64 编译源文件时,这个函数实际上在名称 pwrite 下可用,因此透明地替换了 32 位接口。

2.3. 设置描述符的文件位置

Setting the File Position of a Descriptor

正如您可以使用 fseek 设置流的文件位置一样,您可以使用 lseek 设置描述符的文件位置。这指定了下一次读取或写入操作在文件中的位置。有关文件位置及其含义的更多信息,请参阅文件定位。

要从描述符中读取当前文件位置值,请使用 lseek (desc, 0, SEEK_CUR)。

函数:off_t lseek (int filedes, off_t offset, int whence)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

lseek 函数用于更改带有描述符的文件的文件位置。

whence 参数指定应如何解释偏移量,与 fseek 函数的方式相同,它必须是符号常量 SEEK_SET、SEEK_CUR 或 SEEK_END 之一。

SEEK_SET

指定偏移量是从文件开头算起的字符数。

SEEK_CUR

指定偏移量是从当前文件位置算起的字符数。这个计数可能是正数或负数。

SEEK_END

指定偏移量是从文件末尾算起的字符数。负数指定文件当前范围内的位置;正数指定当前结束后的位置。如果您将位置设置为超过当前结束,并实际写入数据,您将使用零扩展文件到该位置。

lseek 的返回值通常是生成的文件位置,以文件开头的字节为单位。您可以将此功能与 SEEK_CUR 一起使用来读取当前文件位置。

如果要追加到文件,使用 SEEK_END 将文件位置设置为文件的当前结尾是不够的。另一个进程可能会在您搜索之后但在您写入之前写入更多数据,扩展文件以便您写入的位置会破坏他们的数据。而是使用 O_APPEND 操作模式;请参阅 I/O 操作模式。

您可以将文件位置设置为超过文件的当前结尾。这本身并不会使文件更长;lseek 从不更改文件。但是该位置的后续输出将扩展文件。文件前一个结尾和新位置之间的字符用零填充。以这种方式扩展文件会创建一个“洞”:零块实际上并未在磁盘上分配,因此文件占用的空间比看起来要少;然后将其称为“稀疏文件”。

如果文件位置无法更改,或者操作在某些方面无效,则 lseek 返回值 -1。为此函数定义了以下 errno 错误条件:

EBADF

文件不是有效的文件描述符。

EINVAL

whence 参数值无效,或者生成的文件偏移量无效。文件偏移量无效。

ESPIPE

文件对应于无法定位的对象,例如管道、FIFO 或终端设备。(POSIX.1 仅针对管道和 FIFO 指定此错误,但在 GNU 系统上,如果对象不可搜索,您总是会得到 ESPIPE。)

当使用 _FILE_OFFSET_BITS == 64 编译源文件时,lseek 函数实际上是 lseek64 并且类型 off_t 具有 64 位,这使得可以处理长达 2^63 字节的文件。

这个函数是多线程程序中的一个取消点。如果线程在调用 lseek 时分配了一些资源(如内存、文件描述符、信号量或其他),则会出现问题。如果线程被取消,这些资源将保持分配状态,直到程序结束。为了避免这种对 lseek 的调用,应该使用取消处理程序来保护。

lseek 函数是 fseek、fseeko、ftell、ftello 和 rewind 函数的底层原语,它们对流而不是文件描述符进行操作。

函数:off64_t lseek64 (int fields, off64_t offset, int wherece)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数类似于 lseek 函数。不同之处在于 offset 参数的类型是 off64_t 而不是 off_t,这使得在 32 位机器上可以寻址大于 2^31 字节和最多 2^63 字节的文件。文件描述符文件必须使用 open64 打开,否则 off64_t 可能的大偏移量将导致小文件模式下的描述符错误。

当在 32 位机器上使用 _FILE_OFFSET_BITS == 64 编译源文件时,此函数实际上可以在名称 lseek 下使用,因此透明地替换了 32 位接口。

如果您多次打开文件,或者使用 dup 复制一个描述符,则同一文件可以有多个描述符。来自单独调用 open 的描述符具有独立的文件位置;在一个描述符上使用 lseek 对另一个描述符没有影响。例如,

{ 
        
  int d1, d2;
  char buf[4];
  d1 = open ("foo", O_RDONLY);
  d2 = open ("foo", O_RDONLY);
  lseek (d1, 1024, SEEK_SET);
  read (d2, buf, 4);
}

将读取从 foo 的第 1024 个字符开始的四个字符,然后再读取从第 1028 个字符开始的四个字符。

数据类型:off_t

这是一个有符号整数类型,用于表示文件大小。在 GNU C 库中,这种类型并不比 int 更窄。

如果源代码使用 _FILE_OFFSET_BITS == 64 编译,则此类型透明地被 off64_t 替换。

数据类型:off64_t

此类型的使用类似于 off_t。不同之处在于,即使在 32 位机器上(其中 off_t 类型将具有 32 位),off64_t 也具有 64 位,因此能够处理长达 2^63 字节的文件。

当使用 _FILE_OFFSET_BITS == 64 编译时,此类型在名称 off_t 下可用。

存在这些“SEEK_…”常量的别名是为了与旧 BSD 系统兼容。它们在两个不同的头文件中定义:fcntl.h 和 sys/file.h。

L_SET

SEEK_SET 的别名。

L_INCR

SEEK_CUR 的别名。

L_XTND

SEEK_END 的别名。

2.4. 描述符和流

Descriptors and Streams

给定一个打开的文件描述符,您可以使用 fdopen 函数为其创建一个流。您可以使用 fileno 函数获取现有流的基础文件描述符。这些函数在头文件 stdio.h 中声明。

函数:FILE * fdopen (int filedes, const char *opentype)

Preliminary: | MT-Safe | AS-Unsafe heap lock | AC-Unsafe mem lock | See POSIX Safety Concepts.

fdopen 函数为文件描述符文件返回一个新流。

opentype 参数的解释方式与 fopen 函数相同(请参阅打开流),但不允许使用“b”选项;这是因为 GNU 系统不区分文本文件和二进制文件。此外,“w”和“w+”不会导致文件截断;这些仅在打开文件时有效,在这种情况下,文件已经打开。您必须确保 opentype 参数与打开文件描述符的实际模式相匹配。

返回值是新的流。如果无法创建流(例如,如果文件描述符指示的文件模式不允许 opentype 参数指定的访问),则返回空指针。

在其他一些系统中,fdopen 可能无法检测到文件描述符的模式不允许 opentype 指定的访问。GNU C 库总是对此进行检查。

有关显示使用 fdopen 函数的示例,请参阅创建管道。

函数:int fileno (FILE *stream)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数返回与流流关联的文件描述符。如果检测到错误(例如,如果流无效)或者如果流不对文件执行 I/O,则 fileno 返回 -1。

函数:int fileno_unlocked (FILE *stream)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

fileno_unlocked 函数等价于 fileno 函数,只是它在状态为 FSETLOCKING_INTERNAL 时不会隐式锁定流。

这个函数是一个 GNU 扩展。

unistd.h 中还为属于标准流 stdin、stdout 和 stderr 的文件描述符定义了符号常量;请参阅标准流。

STDIN_FILENO

这个宏的值为 0,它是标准输入的文件描述符。

STDOUT_FILENO

这个宏的值为 1,它是标准输出的文件描述符。

STDERR_FILENO

这个宏的值为 2,它是标准错误输出的文件描述符。

2.5. 混合流和描述符的危险

Dangers of Mixing Streams and Descriptors

您可以将多个文件描述符和流(我们简称为流和描述符“通道”)连接到同一个文件,但您必须注意避免通道之间的混淆。有两种情况需要考虑:共享单个文件位置值的链接通道和具有自己文件位置的独立通道。

最好在程序中只使用一个通道来将实际数据传输到任何给定文件,除非所有访问都是用于输入。例如,如果您打开一个管道(您只能在文件描述符级别执行此操作),则使用描述符执行所有 I/O,或者使用 fdopen 从描述符构造一个流,然后使用该流执行所有 I/O .

2.5.1. 链接通道

Linked Channels

来自单个打开的通道共享相同的文件位置;我们称它们为链接频道。当您使用 fdopen 从描述符创建流时,当您从具有 fileno 的流中获取描述符时,当您使用 dup 或 dup2 复制描述符时,以及在 fork 期间继承描述符时,会产生链接通道。对于不支持随机访问的文件,例如终端和管道,所有通道都有效链接。在随机访问文件上,所有附加类型的输出流都有效地相互链接。

如果您一直在使用流进行 I/O(或刚刚打开流),并且想要使用与其链接的另一个通道(流或描述符)进行 I/O,则必须首先清理您一直在使用的流。请参阅清洗流。

终止一个进程,或在该进程中执行一个新程序,会破坏该进程中的所有流。如果链接到这些流的描述符在其他进程中持续存在,则它们的文件位置将因此变得未定义。为了防止这种情况,您必须在销毁流之前清理它们。

2.5.2. 独立通道

Independent Channels

当您在可搜索文件上单独打开通道(流或描述符)时,每个通道都有自己的文件位置。这些被称为独立通道。

系统独立处理每个通道。大多数时候,这是非常可预测和自然的(尤其是对于输入):每个通道都可以在文件中自己的位置顺序读取或写入。但是,如果某些通道是流,则必须采取以下预防措施:

  • 您应该在使用后清理输出流,然后再执行任何可能从文件的同一部分读取或写入的操作。
  • 您应该在读取可能已使用独立通道修改的数据之前清理输入流。否则,您可能会读取流缓冲区中的过时数据。

如果您确实在文件末尾输出到一个通道,这肯定会使其他独立通道位于新结束之前的某个位置。在写入之前,您无法可靠地将它们的文件位置设置为文件的新结尾,因为在设置文件位置和写入数据之间,文件总是可以被另一个进程扩展。相反,使用附加类型的描述符或流;它们总是在文件的当前末尾输出。为了使文件结束位置准确,您必须清理您正在使用的输出通道,如果它是一个流。

对于不支持随机访问的文件,两个通道不可能有单独的文件指针。因此,用于读取或写入此类文件的通道始终是链接的,而不是独立的。附加类型的通道也总是链接的。对于这些频道,请遵循链接频道的规则;请参阅链接通道。

2.5.3. 清洗流

Cleaning Streams

在大多数情况下,您可以使用 fflush 来清理流。

如果您知道流已经干净,则可以跳过 fflush。只要缓冲区为空,流就是干净的。例如,无缓冲的流始终是干净的。位于文件末尾的输入流是干净的。当最后一个字符输出是换行符时,行缓冲流是干净的。但是,刚刚打开的输入流可能不干净,因为它的输入缓冲区可能不是空的。

在一种情况下,在大多数系统上清理流是不可能的。这是当流从非随机访问的文件中进行输入时。这样的流通常会提前读取,并且当文件不是随机访问时,没有办法返回已经读取的多余数据。当输入流从随机访问文件中读取时, fflush 确实会清理流,但会将文件指针留在不可预知的位置;您必须在进行任何进一步的 I/O 之前设置文件指针。

关闭仅输出流也会执行 fflush,因此这是清理输出流的有效方法。

在使用其描述符进行控制操作(例如设置终端模式)之前,您无需清理流;这些操作不会影响文件位置,也不受文件位置的影响。您可以对这些操作使用任何描述符,并且所有通道都会同时受到影响。但是,已经“输出”到流但仍由流缓冲的文本在随后刷新时将受制于新的终端模式。为确保“过去”输出被当时有效的终端设置覆盖,请在设置模式之前刷新该终端的输出流。请参阅终端模式。

2.6. 快速分散-聚集 I/O

Fast Scatter-Gather I/O

一些应用程序可能需要读取或写入数据到多个缓冲区,这些缓冲区在内存中是分开的。尽管这可以通过多次调用读取和写入来轻松完成,但效率低下,因为每次内核调用都会产生开销。

相反,许多平台提供了特殊的高速原语来在单个内核调用中执行这些分散-聚集操作。GNU C 库将在缺少这些原语的任何系统上提供仿真,因此它们不是可移植性威胁。它们在 sys/uio.h 中定义。

这些函数由 iovec 结构数组控制,这些结构描述了每个缓冲区的位置和大小。

数据类型:struct iovec iovec 结构描述了一个缓冲区。它包含两个字段:

void *iov_base

包含缓冲区的地址。

size_t iov_len

包含缓冲区的长度。

函数:ssize_t readv (int fields, const struct iovec *vector, int count)

Preliminary: | MT-Safe | AS-Unsafe heap | AC-Unsafe mem | See POSIX Safety Concepts.

readv 函数从文件中读取数据并将其分散到向量中描述的缓冲区中,该缓冲区被视为长计数结构。随着每个缓冲区被填满,数据被发送到下一个缓冲区。

请注意,不保证 readv 会填满所有缓冲区。出于与 read 相同的原因,它可能会在任何时候停止。

返回值是读取的字节数(不是缓冲区),0 表示文件结束,-1 表示错误。可能的错误与读取中的相同。

函数:ssize_t writev (int fields, const struct iovec *vector, int count)

Preliminary: | MT-Safe | AS-Unsafe heap | AC-Unsafe mem | See POSIX Safety Concepts.

writev 函数从向量中描述的缓冲区收集数据,这些数据被视为长计数结构,并将它们写入文件。随着每个缓冲区的写入,它会移动到下一个缓冲区。

与 readv 一样,writev 可能会在与 write 相同的条件下中途停止。

返回值是写入的字节数,或 -1 表示错误。可能的错误与写入相同。

函数:ssize_t preadv (int fd, const struct iovec *iov, int iovcnt, off_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该函数类似于 readv 函数,不同之处在于它增加了一个类似于 pread 的 off_t 类型的额外偏移参数。从位置偏移开始的文件中读取数据。文件描述符本身的位置不受操作的影响。该值与调用前相同。

当源文件使用 _FILE_OFFSET_BITS == 64 编译时,preadv 函数实际上是 preadv64 并且类型 off_t 有 64 位,这使得处理长达 2^63 字节的文件成为可能。

返回值是读取的字节数(不是缓冲区),0 表示文件结束,-1 表示错误。可能的错误与 readv 和 pread 中的相同。

函数:ssize_t preadv64 (int fd, const struct iovec *iov, int iovcnt, off64_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该函数与 preadv 函数类似,不同之处在于 offset 参数的类型为 off64_t 而不是 off_t。它可以在 32 位机器上处理大于 2^31 字节和最多 2^63 字节的文件。文件描述符文件必须使用 open64 打开,否则 off64_t 可能的大偏移量将导致小文件模式下的描述符错误。

当在 32 位机器上使用 _FILE_OFFSET_BITS == 64 编译源文件时,这个函数实际上在名称 preadv 下可用,因此透明地替换了 32 位接口。

函数:ssize_t pwritev (int fd, const struct iovec *iov, int iovcnt, off_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该函数类似于 writev 函数,不同之处在于它增加了一个类似于 pwrite 的 off_t 类型的额外偏移参数。数据从位置偏移开始写入文件。文件描述符本身的位置不受操作的影响。该值与调用前相同。

但是,在 Linux 上,如果使用 O_APPEND 打开文件,则 pwrite 会将数据附加到文件的末尾,而不管 offset 的值如何。

当使用 _FILE_OFFSET_BITS == 64 编译源文件时,pwritev 函数实际上是 pwritev64 并且类型 off_t 有 64 位,这使得处理长达 2^63 字节的文件成为可能。

返回值是写入的字节数(不是缓冲区),0 表示文件结束,-1 表示错误。可能的错误与 writev 和 pwrite 中的相同。

函数:ssize_t pwritev64 (int fd, const struct iovec *iov, int iovcnt, off64_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该函数与 pwritev 函数类似,不同之处在于 offset 参数的类型为 off64_t 而不是 off_t。它可以在 32 位机器上处理大于 2^31 字节和最多 2^63 字节的文件。文件描述符文件必须使用 open64 打开,否则 off64_t 可能的大偏移量将导致小文件模式下的描述符错误。

当在 32 位机器上使用 _FILE_OFFSET_BITS == 64 编译源文件时,这个函数实际上在名称 pwritev 下可用,因此透明地替换了 32 位接口。

函数:ssize_t preadv2 (int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数类似于 preadv 函数,不同之处在于它添加了一个额外的 int 类型的 flags 参数。此外,如果 offset 为 -1,则使用并更新当前文件位置(如 readv 函数)。

支持的标志取决于底层系统。对于 Linux,它支持:

RWF_HIPRI

高优先级请求。这会添加一个标志,告诉文件系统这是一个值得轮询硬件的高优先级请求。该标志纯粹是建议性的,如果不支持可以忽略。fd 必须使用 O_DIRECT 打开。

RWF_DSYNC

每 IO 同步,就像使用 O_DSYNC 标志打开文件一样。

RWF_SYNC

每 IO 同步,就像使用 O_SYNC 标志打开文件一样。

RWF_NOWAIT

对这个操作使用非阻塞模式;也就是说,如果操作会阻塞,对 preadv2 的调用将失败并将 errno 设置为 EAGAIN。

RWF_APPEND

Per-IO 同步,就像使用 O_APPEND 标志打开文件一样。

当源文件使用 _FILE_OFFSET_BITS == 64 编译时,preadv2 函数实际上是 preadv64v2 并且类型 off_t 有 64 位,这使得处理长达 2^63 字节的文件成为可能。

返回值是读取的字节数(不是缓冲区),0 表示文件结束,-1 表示错误。可能的错误与 preadv 中的相同,但添加了:

EOPNOTSUPP

使用了不受支持的标志。

函数:ssize_t preadv64v2 (int fd, const struct iovec *iov, int iovcnt, off64_t offset, int flags)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该函数与 preadv2 函数类似,不同之处在于 offset 参数的类型为 off64_t 而不是 off_t。它可以在 32 位机器上处理大于 2^31 字节和最多 2^63 字节的文件。文件描述符文件必须使用 open64 打开,否则 off64_t 可能的大偏移量将导致小文件模式下的描述符错误。

当在 32 位机器上使用 _FILE_OFFSET_BITS == 64 编译源文件时,这个函数实际上在名称 preadv2 下可用,因此透明地替换了 32 位接口。

函数:ssize_t pwritev2 (int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数类似于 pwritev 函数,不同之处在于它添加了一个额外的 int 类型的标志参数。此外,如果 offset 为 -1,则应使用和更新当前文件位置(如 writev 函数)。

支持的标志取决于底层系统。对于 Linux,支持的标志与 preadv2 的相同。

当使用 _FILE_OFFSET_BITS == 64 编译源文件时,pwritev2 函数实际上是 pwritev64v2 并且类型 off_t 有 64 位,这使得处理长达 2^63 字节的文件成为可能。

返回值是写入的字节数(不是缓冲区),0 表示文件结束,-1 表示错误。可能的错误与 preadv2 中的相同。

函数:ssize_t pwritev64v2 (int fd, const struct iovec *iov, int iovcnt, off64_t offset, int flags)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该函数与 pwritev2 函数类似,不同之处在于 offset 参数的类型为 off64_t 而不是 off_t。它可以在 32 位机器上处理大于 2^31 字节和最多 2^63 字节的文件。文件描述符文件必须使用 open64 打开,否则 off64_t 可能的大偏移量将导致小文件模式下的描述符错误。

当在 32 位机器上使用 _FILE_OFFSET_BITS == 64 编译源文件时,这个函数实际上在名称 pwritev2 下可用,因此透明地替换了 32 位接口。

2.7. 在两个文件之间复制数据

Copying data between two files

提供了一种特殊功能,用于在同一文件系统上的两个文件之间复制数据。系统可以优化这样的复制操作。这在网络文件系统上尤为重要,否则数据必须通过网络传输两次。

请注意,此函数仅复制文件数据,而不会复制文件权限或扩展属性等元数据。

函数:ssize_t copy_file_range (int inputfd, off64_t *inputpos, int outputfd, off64_t *outputpos, ssize_t length, unsigned int flags)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数将文件描述符 inputfd 中的最大长度字节复制到文件描述符 outputfd。

该函数可以对当前文件位置(如读取和写入)和显式偏移量(如 pread 和 pwrite)进行操作。如果 inputpos 指针为空,则将 inputfd 的文件位置作为复制操作的起点,并在此过程中推进文件位置。如果inputpos不为null,则*inputpos作为复制操作的起点,*inputpos按复制的字节数递增,但文件位置保持不变。类似的规则适用于输出文件位置的 outputfd 和 outputpos。

flags 参数当前是保留的,必须为零。

copy_file_range 函数返回复制的字节数。如果输入文件包含的剩余字节数少于长度,或者发生读取或写入失败,这可能小于指定的长度。如果立即遇到输入文件的结尾,则返回值为零。

如果没有字节可以被复制,为了报告错误,copy_file_range 返回值 -1 并设置 errno。下表列出了此函数的一些错误情况。

ENOSYS

内核没有实现所需的功能。

EISDIR

描述符 inputfd 或 outputfd 中的至少一个是指目录。

EINVAL

描述符 inputfd 或 outputfd 中的至少一个是指非常规、非目录文件(例如套接字或 FIFO)。

之前的输入或输出位置在复制操作之后超出了实现定义的限制。

flags 参数不为零。

EFBIG

新文件大小将超过进程文件大小限制。请参阅限制资源使用。

之前的输入或输出位置在复制操作之后超出了实现定义的限制。如果在 32 位计算机上未使用大文件支持 (LFS) 打开文件,则可能会发生这种情况,并且复制操作会创建一个大于 off_t 可以表示的文件。

EBADF

参数 inputfd 不是为读取而打开的有效文件描述符。

参数 outputfd 不是为写入而打开的有效文件描述符,或者 outputfd 已使用 O_APPEND 打开。

此外,copy_file_range 可能会因 read、pread、write 和 pwrite 使用的错误代码而失败。

copy_file_range 函数是一个取消点。在取消的情况下,输入位置(文件位置或 *inputpos 处的值)是不确定的。

2.8. 内存映射I/O

Memory-mapped I/O

在现代操作系统上,可以将文件映射(读作“em-map”)到内存区域。完成后,可以像程序中的数组一样访问文件。

这比读取或写入更有效,因为仅加载程序实际访问的文件区域。对 mmapped 区域的尚未加载部分的访问与换出页面的处理方式相同。

由于当物理内存不足时,可以将映射的页面存储回它们的文件,因此可以将文件映射到比物理内存和交换空间大几个数量级的数量级。唯一的限制是地址空间。32 位机器上的理论限制为 4GB - 但是,实际限制会更小,因为某些区域将保留用于其他目的。如果使用 LFS 接口,则 32 位系统上的文件大小不限于 2GB(带符号的偏移量将 4GB 的可寻址区域减少了一半);完整的 64 位可用。

内存映射仅适用于整个内存页面。因此,映射地址必须是页面对齐的,并且长度值将向上取整。要确定机器使用的页面的默认大小,应该使用:

size_t page_size = (size_t) sysconf (_SC_PAGESIZE);

在某些系统上,映射可以为某些文件使用更大的页面大小,并且应用程序也可以为匿名映射请求更大的页面大小(参见下面的 MAP_HUGETLB 标志)。

在 sys/mman.h 中声明了以下函数:

函数:void * mmap (void *address, size_t length, int protect, int flags, int fields, off_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

mmap 函数创建一个新映射,连接到文件中打开的文件中的字节(offset)到(offset + length - 1)。为由filedes 指定的文件创建一个新引用,关闭文件不会删除该引用。

address 给出映射的首选起始地址。NULL 表示没有偏好。该地址的任何先前映射都会被自动删除。您提供的地址可能仍会更改,除非您使用 MAP_FIXED 标志。

Protect 包含控制允许访问类型的标志。它们包括 PROT_READ、PROT_WRITE 和 PROT_EXEC。特殊标志 PROT_NONE 保留一个地址空间区域以供将来使用。mprotect 函数可用于更改保护标志。请参阅内存保护。

flags 包含控制地图性质的标志。必须指定 MAP_SHARED 或 MAP_PRIVATE 之一。

它们包括:

MAP_PRIVATE

这指定了对区域的写入永远不应写回附加文件。相反,会为该进程制作一个副本,如果内存不足,该区域将正常交换。没有其他进程会看到这些更改。

由于私有映射在写入时有效地恢复为普通内存,因此如果将此模式与 PROT_WRITE 一起使用,则必须有足够的虚拟内存来复制整个映射区域。

MAP_SHARED

这指定对该区域的写入将被写回文件。所做的更改将立即与映射同一文件的其他进程共享。

请注意,实际写入可能随时发生。如果使用常规 I/O 的其他进程获得文件的一致视图很重要,则需要使用 msync,如下所述。

MAP_FIXED

这会强制系统使用 address 中指定的确切映射地址,如果不能,则失败。

MAP_ANONYMOUS

MAP_ANON

这个标志告诉系统创建一个匿名映射,而不是连接到一个文件。文件和偏移量被忽略,该区域用零初始化。

在某些系统上,匿名映射被用作扩展堆的基本原语。它们对于在不创建文件的情况下在多个任务之间共享数据也很有用。

在某些系统上,使用私有匿名 mmap 比使用 malloc 处理大块更有效。这不是 GNU C 库的问题,因为包含的 malloc 会在适当的地方自动使用 mmap。

MAP_HUGETLB

这要求系统使用大于映射的默认页面大小的替代页面大小。对于某些工作负载,增加大型映射的页面大小可以提高性能,因为系统需要处理的页面要少得多。对于需要在存储或不同节点之间频繁传输页面的其他工作负载,降低的页面粒度可能会由于页面大小的增加和更大的传输而导致性能问题。

为了创建映射,系统需要增加物理上连续内存大小的页面大小。因此,MAP_HUGETLB 映射会受到内存碎片的影响,即使系统中有足够的可用内存,它们的创建也会失败。

并非所有文件系统都支持增加页面大小的映射。

MAP_HUGETLB 标志特定于 Linux。

mmap 返回新映射的地址,或 MAP_FAILED 表示错误。

可能的错误包括:

EINVAL

要么地址不可用(因为它不是适用页面大小的倍数),要么给出了不一致的标志。

如果指定了 MAP_HUGETLB,则文件或系统不支持大页面大小。

EACCES

对于保护中指定的访问类型,filedes 未打开。

ENOMEM

要么没有足够的内存用于操作,要么进程的地址空间不足。

ENODEV

此文件属于不支持映射的类型。

ENOEXEC

该文件位于不支持映射的文件系统上。

函数:void * mmap64 (void *address, size_t length, int protect, int flags, int fields, off64_t offset)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

mmap64 函数等价于 mmap 函数,但 offset 参数的类型为 off64_t。在 32 位系统上,这允许与文件描述符关联的文件大于 2GB。filedes 必须是从调用 open64 或 fopen64 和 freopen64 返回的描述符,其中使用 fileno 检索描述符。

当使用 _FILE_OFFSET_BITS == 64 翻译源代码时,此函数实际上在名称 mmap 下可用。即,使用 64 位文件大小和偏移量的新扩展 API 透明地替换了旧 API。

函数:int munmap (void *addr, size_t length)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

munmap 删除从 (addr) 到 (addr + length) 的所有内存映射。length 应该是映射的长度。

在一个命令中取消映射多个映射是安全的,或者在范围中包含未映射的空间。也可以只取消现有映射的一部分。但是,只能删除整个页面。如果长度不是偶数页,则会向上取整。

它返回 0 表示成功,返回 -1 表示错误。

一个错误是可能的:

EINVAL

给定的内存范围超出了用户 mmap 范围或未对齐页面。

函数:int msync (void *address, size_t length, int flags)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

使用共享映射时,内核可以在删除映射之前的任何时间写入文件。为了确定数据实际上已经写入文件并且可以被非内存映射 I/O 访问,有必要使用这个函数。

它在区域地址上操作到(地址+长度)。它可以用于映射的一部分或多个映射,但是给定的区域不应包含任何未映射的空间。

标志可以包含一些选项:

MS_SYNC

此标志确保数据实际写入磁盘。通常 msync 只确保使用常规 I/O 访问文件反映最近的更改。

MS_ASYNC

这告诉 msync 开始同步,但不要等待它完成。

msync 返回 0 表示成功,返回 -1 表示错误。错误包括:

EINVAL

给出了无效的区域,或者标志无效。

EFAULT

在给定区域的至少一部分中不存在现有映射。

函数:void * mremap (void *address, size_t length, size_t new_length, int flag)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数可用于更改现有内存区域的大小。地址和长度必须覆盖在同一 mmap 语句中完全映射的区域。将返回具有相同特征的新映射,其长度为 new_length。

一个选项是可能的,MREMAP_MAYMOVE。如果它在标志中给出,系统可能会删除现有的映射并在另一个位置创建一个具有所需长度的新映射。

返回结果映射的地址,或 -1。可能的错误代码包括:

EFAULT

在原始区域的至少一部分中不存在现有映射,或者该区域覆盖了两个或多个不同的映射。

EINVAL

给出的地址未对齐或不合适。

EAGAIN

该区域已锁定页面,如果扩展,它将超过进程对锁定页面的资源限制。请参阅限制资源使用。

ENOMEM

该区域是私有可写的,并且没有足够的虚拟内存来扩展它。此外,如果未给出 MREMAP_MAYMOVE 并且扩展将与另一个映射区域发生冲突,则会发生此错误。

此功能仅在少数系统上可用。除了执行可选优化之外,不应依赖此功能。

并非所有文件描述符都可以被映射。套接字、管道和大多数设备只允许顺序访问,不适合映射抽象。此外,一些常规文件可能无法映射,旧内核可能根本不支持映射。因此,使用 mmap 的程序应该有一个在失败时使用的备用方法。请参阅 GNU 编码标准中的 Mmap。

函数:int madvise (void *addr, size_t length, int advice)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数可用于向系统提供有关从 addr 开始并扩展 length 字节的内存区域的预期使用模式的建议。

建议的有效 BSD 值是:

MADV_NORMAL

该地区不应受到进一步的特殊待遇。

MADV_RANDOM

该区域将通过随机页面引用访问。内核应该为每个页面错误分页最小数量的页面。

MADV_SEQUENTIAL

该区域将通过顺序页面引用进行访问。这可能会导致内核积极预读,期望在该区域内的任何页面错误之后进一步的顺序引用。

MADV_WILLNEED

该地区将是必要的。该区域内的页面可能已被内核预先插入。

MADV_DONTNEED

不再需要该区域。内核可能会释放这些页面,导致对页面的任何更改丢失,以及交换出的页面被丢弃。

MADV_HUGEPAGE

表明增加此映射的页面大小是有益的。这可以提高较大映射的性能,因为系统需要处理的页面要少得多。但是,如果部分映射在存储或不同节点之间频繁传输,则性能可能会受到影响,因为单个传输可能会因页面大小的增加而变得相当大。

此标志特定于 Linux。

MADV_NOHUGEPAGE

撤消先前 MADV_HUGEPAGE 建议的效果。此标志特定于 Linux。

POSIX 名称略有不同,但含义相同:

POSIX_MADV_NORMAL

这对应于 BSD 的 MADV_NORMAL。

POSIX_MADV_RANDOM

这对应于 BSD 的 MADV_RANDOM。

POSIX_MADV_SEQUENTIAL

这对应于 BSD 的 MADV_SEQUENTIAL。

POSIX_MADV_WILLNEED

这对应于 BSD 的 MADV_WILLNEED。

POSIX_MADV_DONTNEED

这对应于 BSD 的 MADV_DONTNEED。

madvise 返回 0 表示成功,返回 -1 表示错误。错误包括:

EINVAL

给出了无效的区域,或者建议无效。

EFAULT

在给定区域的至少一部分中不存在现有映射。

函数:int shm_open (const char *name, int oflag, mode_t mode)

Preliminary: | MT-Safe locale | AS-Unsafe init heap lock | AC-Unsafe lock mem fd | See POSIX Safety Concepts.

此函数返回一个文件描述符,可用于通过 mmap 分配共享内存。不相关的进程可以使用相同的名称来创建或打开现有的共享内存对象。

name 参数指定要打开的共享内存对象。在 GNU C 库中,它必须是小于 NAME_MAX 字节的字符串,以可选的斜杠开头,但不包含其他斜杠。

oflag 和 mode 参数的语义与 open 中的相同。

shm_open 成功时返回文件描述符,错误时返回 -1。失败时设置 errno。

函数:int shm_unlink (const char *name)

Preliminary: | MT-Safe locale | AS-Unsafe init heap lock | AC-Unsafe lock mem fd | See POSIX Safety Concepts.

此函数是 shm_open 的逆函数,它删除之前由 shm_open 创建的具有给定名称的对象。

shm_unlink 成功返回 0 或错误返回 -1。失败时设置 errno。

函数:int memfd_create (const char *name, unsigned int flags)

Preliminary: | MT-Safe |

标签: 插接式连接器mfd001插接式连接器mfd014

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台