glibc 知:手册11:输入/输出概述

1. 前言

The GNU C Library Reference Manual for version 2.35

2. 输入/输出概述

Input/Output Overview

大多数程序需要进行输入(读取数据)或输出(写入数据),或者最常见的情况是两者都进行,以执行任何有用的操作。 GNU C 库提供了如此多的输入和输出函数选择,最难的部分通常是决定哪个函数最合适!

本章介绍与输入和输出相关的概念和术语。其他与 GNU I/O 工具相关的章节是:

输入/输出流,涵盖了对流进行操作的高级函数,包括格式化的输入和输出。
底层输入/输出,涵盖文件描述符的基本 I/O 和控制功能。
文件系统接口,涵盖对目录进行操作和操作文件属性(如访问模式和所有权)的功能。
管道和 FIFO,其中包括有关基本进程间通信设施的信息。
套接字,它涵盖了更复杂的进程间通信设施,支持网络。
底层终端接口,涵盖更改输入和输出到终端或其他串行设备的处理方式的功能。

2.1. 输入/输出概念

Input/Output Concepts

在您可以读取或写入文件的内容之前,您必须建立到该文件的连接或通信通道。 这个过程称为打开文件。 您可以打开文件进行读取、写入或两者兼而有之。

与打开文件的连接表示为流或文件描述符。 您将此作为参数传递给执行实际读取或写入操作的函数,以告诉它们要操作哪个文件。 某些函数需要流,而其他函数旨在对文件描述符进行操作。

当您完成对文件的读取或写入后,您可以通过关闭文件来终止连接。 一旦关闭了流或文件描述符,就不能再对其进行任何输入或输出操作。

2.1.1. 流和文件描述符

Streams and File Descriptors

当您想要对文件进行输入或输出时,您可以选择两种基本机制来表示程序和文件之间的连接:文件描述符和流。文件描述符表示为 int 类型的对象,而流表示为 FILE * 对象。

文件描述符为输入和输出操作提供了一个原始的低级接口。文件描述符和流都可以表示与设备(例如终端)的连接,或用于与另一个进程通信的管道或套接字,以及普通文件。但是,如果您想要执行特定于特定类型设备的控制操作,则必须使用文件描述符;没有以这种方式使用流的设施。如果您的程序需要在特殊模式下进行输入或输出,例如非阻塞(或轮询)输入(请参阅文件状态标志),您还必须使用文件描述符。

流提供了一个更高级别的接口,位于原始文件描述符设施之上。流接口处理所有类型的文件非常相似——唯一的例外是您可以选择的三种缓冲样式(请参阅流缓冲)。

使用流接口的主要优点是,用于对流执行实际输入和输出操作(与控制操作相反)的一组函数比文件描述符的相应工具更丰富、更强大。文件描述符接口只提供了传输字符块的简单函数,但流接口还提供了强大的格式化输入和输出函数(printf 和 scanf)以及面向字符和行的输入和输出函数。

由于流是根据文件描述符实现的,因此您可以从流中提取文件描述符并直接对文件描述符执行低级操作。您还可以最初将连接作为文件描述符打开,然后创建与该文件描述符关联的流。

一般来说,你应该坚持使用流而不是文件描述符,除非你想做一些只能在文件描述符上完成的特定操作。如果您是初学者并且不确定要使用哪些函数,我们建议您专注于格式化输入函数(参见格式化输入)和格式化输出函数(参见格式化输出)。

如果您担心您的程序对 GNU 以外的系统的可移植性,您还应该知道文件描述符不像流那样可移植。您可以期望任何运行 ISO C 的系统都支持流,但非 GNU 系统可能根本不支持文件描述符,或者可能只实现对文件描述符进行操作的 GNU 函数的子集。然而,GNU C 库中的大多数文件描述符函数都包含在 POSIX.1 标准中。

2.1.2. 文件位置

File Position

打开文件的属性之一是它的文件位置,它跟踪文件中下一个字符将被读取或写入的位置。在 GNU 系统和所有 POSIX.1 系统上,文件位置只是一个整数,表示从文件开头开始的字节数。

文件位置通常在打开文件时设置为文件的开头,每次读取或写入字符时,文件位置都会增加。换句话说,对文件的访问通常是顺序的。

普通文件允许在文件中的任何位置进行读取或写入操作。一些其他类型的文件也可能允许这样做。允许这样做的文件有时称为随机访问文件。您可以使用流上的 fseek 函数(请参阅文件定位)或文件描述符上的 lseek 函数(请参阅输入和输出原语)更改文件位置。如果您尝试更改不支持随机访问的文件上的文件位置,则会收到 ESPIPE 错误。

为追加访问而打开的流和描述符被特别对待以用于输出:到此类文件的输出总是按顺序追加到文件的末尾,而不管文件位置如何。但是,文件位置仍用于控制文件读取的位置。

如果你仔细想想,你会发现几个程序可以同时读取一个给定的文件。为了让每个程序能够以自己的速度读取文件,每个程序都必须有自己的文件指针,它不受其他程序所做的任何事情的影响。

事实上,文件的每次打开都会创建一个单独的文件位置。因此,如果即使在同一个程序中打开文件两次,也会得到两个具有独立文件位置的流或描述符。

相反,如果你打开一个描述符,然后复制它以获得另一个描述符,这两个描述符共享相同的文件位置:改变一个描述符的文件位置会影响另一个。

2.2. 文件名

File Names

为了打开与文件的连接,或执行其他操作(例如删除文件),您需要某种方式来引用该文件。 几乎所有文件的名称都是字符串,即使文件实际上是磁带驱动器或终端等设备。 这些字符串称为文件名。 您指定文件名来说明您要打开或操作哪个文件。

本节介绍文件名的约定以及操作系统如何使用它们。

2.2.1. 目录

Directories

为了理解文件名的语法,您需要了解文件系统是如何组织成目录层次结构的。

目录是一个文件,其中包含将其他文件与名称相关联的信息;这些关联称为链接或目录条目。有时,人们会说“目录中的文件”,但实际上,目录只包含指向文件的指针,而不包含文件本身。

包含在目录条目中的文件的名称称为文件名组件。通常,文件名由一个或多个此类组件的序列组成,由斜线字符 (‘/’) 分隔。只是一个组件的文件名相对于其目录命名文件。具有多个组件的文件名命名一个目录,然后是该目录中的一个文件,依此类推。

其他一些文档,例如 POSIX 标准,使用术语路径名来表示我们称为文件名的内容,而使用文件名或路径名组件来表示本手册所称的文件名组件。我们不使用这个术语,因为“路径”是完全不同的东西(要搜索的目录列表),我们认为用于其他东西的“路径名”会使用户感到困惑。在 GNU 文档中,我们总是使用“文件名”和“文件名组件”(或者有时只是“组件”,上下文很明显)。一些宏在其名称中使用 POSIX 术语,例如 PATH_MAX。这些宏是由 POSIX 标准定义的,所以我们不能更改它们的名称。

您可以在文件系统接口中找到有关目录操作的更多详细信息。

2.2.2. 文件名解析

File Name Resolution

文件名由以斜杠 (‘/’) 字符分隔的文件名组件组成。在 GNU C 库支持的系统上,多个连续的“/”字符等同于单个“/”字符。

确定文件名所指的文件的过程称为文件名解析。这是通过按从左到右的顺序检查构成文件名的组件并在前一个组件命名的目录中定位每个后续组件来执行的。当然,作为目录引用的每个文件都必须实际存在,是目录而不是常规文件,并且具有进程可以访问的适当权限;否则文件名解析失败。

如果文件名以“/”开头,则文件名中的第一个组件位于进程的根目录中(通常系统上的所有进程都具有相同的根目录)。这样的文件名称为绝对文件名。

否则,文件名中的第一个组件位于当前工作目录中(请参阅工作目录)。这种文件名称为相对文件名。

文件名组成部分。 (“dot”) 和 … (“dot-dot”) 具有特殊含义。每个目录都有这些文件名组件的条目。文件名组件。指目录本身,而文件名组件 … 指其父目录(包含相关目录链接的目录)。作为一种特殊情况,根目录中的 … 指的是根目录本身,因为它没有父目录;因此 /… 与 / 相同。

以下是文件名的一些示例:

/a
在根目录中名为 a 的文件。

/a/b
名为 b 的文件,位于根目录中名为 a 的目录中。

a
当前工作目录中名为 a 的文件。

/a/./b
这与 /a/b 相同。

./a
当前工作目录中名为 a 的文件。

…/a
当前工作目录的父目录中名为 a 的文件。

命名目录的文件名可以选择以“/”结尾。可以指定 / 的文件名来引用根目录,但空字符串不是有意义的文件名。如果要引用当前工作目录,请使用 .或者 ./。

与其他一些操作系统不同,GNU 系统在其文件名语法中没有对文件类型(或扩展名)或文件版本的任何内置支持。许多程序和实用程序对文件名使用约定——例如,包含 C 源代码的文件的名称通常以“.c”为后缀——但文件系统本身并没有强制执行这种约定。

2.2.3. 文件名错误

File Name Errors

接受文件名参数的函数通常会检测这些与文件名语法相关的 errno 错误条件或查找命名文件的问题。这些错误在本手册中被称为通常的文件名错误。

EACCES

该进程没有文件名的目录组件的搜索权限。

ENAMETOOLONG

当文件名的总长度大于 PATH_MAX 或单个文件名组件的长度大于 NAME_MAX 时,将使用此错误。请参阅文件系统容量限制。

在 GNU/Hurd 系统上,对整体文件名长度没有强加限制,但某些文件系统可能会对组件的长度进行限制。

ENOENT

当文件名中作为目录组件引用的文件不存在,或者组件是目标文件不存在的符号链接时,会报此错误。请参阅符号链接。

ENOTDIR

在文件名中作为目录组件引用的文件存在,但它不是目录。

ELOOP

尝试查找文件名时解析了太多符号链接。系统对在查找单个文件名时可以解析的符号链接的数量有任意限制,作为检测循环的原始方法。请参阅符号链接。

2.2.4. 文件名的可移植性

Portability of File Names

文件名中讨论的文件名语法规则是 GNU 系统和其他 POSIX 系统通常使用的规则。但是,其他操作系统可能使用其他约定。

了解文件名可移植性问题对您很重要有两个原因:

  • 如果您的程序对文件名语法做出假设,或者包含嵌入的文字文件名字符串,则更难让它在使用不同语法约定的其他操作系统下运行。
  • 即使您不担心在运行其他操作系统的机器上运行程序,仍然可以访问使用不同命名约定的文件。例如,您可能能够通过网络访问另一台运行不同操作系统的计算机上的文件系统,或者以其他操作系统使用的格式读写磁盘。

ISO C 标准对文件名语法几乎没有说明,只是文件名是字符串。除了对文件名长度和文件名中可以​​有效出现的字符的不同限制之外,不同的操作系统对结构化目录和文件类型或扩展名等概念使用不同的约定和语法。某些操作系统可能支持某些概念(例如文件版本),而其他操作系统可能不支持。

POSIX.1 标准允许实现对文件名语法施加额外的限制,包括文件名中允许使用的字符以及文件名和文件名组件字符串的长度。但是,在 GNU 系统上,除了空字符之外的任何字符都可以在文件名字符串中使用,并且在 GNU/Hurd 系统上,文件名字符串的长度没有限制。

3. 参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值