输入和输出处理

本主题提供为编程提供有关输入和输出处理以及输入和输出 (I/O) 处理子例程的介绍。

I/O 库子例程可以将数据发送至设备或文件,也可以从设备或文件发送数据。 系统将设备作为 I/O 文件处理。 例如,必须如同处理文件那样打开和关闭设备。

一些子例程使用标准输入和标准输出作为它们的 I/O 通道。 然而,对于大部分子例程,可以为数据传输的源或目标指定不同的文件。 对于一些子例程,可以使用指向包含文件名称的结构的文件指针;对于其他子例程,可以使用文件描述符(即在文件打开时分配给其的正整数)。

存储在 C 库 (libc.a) 中的 I/O 子例程提供流 I/O。 要访问这些流 I/O 子例程,必须使用以下语句包含 stdio.h 文件:
#include <stdio.h>

一些 I/O 库子例程是定义在头文件中的宏,一些是函数的对象模块。 在许多情况中,库包含执行相同类型操作的宏和函数。 决定使用宏还是函数时,应考虑下列情况:

  • 不能使用 dbx 程序为宏设置断点。
  • 宏通常比它们相当的函数快,因为预处理器使用程序中实际代码行替换宏。
  • 宏在编译后产生较大的对象代码。
  • 函数可能会产生要避免的副作用。

I/O 处理中使用的文件、命令和子例程提供下列接口:

低层
低层接口为文件和设备提供基本的打开和关闭函数。
流接口为管道和 FIFO 提供读写 I/O。
终端
终端接口提供格式化的输出和缓冲。
异步
异步接口提供并发的 I/O 和处理。
输入语言
输入语言接口使用 lexyacc 命令为解释 I/O 生成词法分析器和解析器程序。

低层 I/O 接口

低层 I/O 接口进入内核的直接入口点,提供诸如打开文件、读写文件和关闭文件等函数。

line 命令提供了允许读取标准输入中的一行的接口,并且以下子例程提供了其他低级别 I/O 函数:

open, openx 或 creat
准备文件或其他路径对象用于通过分配的文件描述符进行读写操作。
read, readx , readv 或 readvx
从打开的文件描述符读取
write, writex , writev 或 writevx
写到打开的文件描述符
CLOSE
放弃文件描述符

Opencreat 子例程在三个系统表中建立条目。 文件描述符作为第一个表的索引,此表作为每个进程数据区,可以由读写子例程进行访问。 此表中的每个条目都具有指向第二个表中相应条目的指针。

第二个表是每个系统的数据库或文件表,允许在多个进程之间共享打开的文件。 此表中的条目指示文件是否打开用于读、写或作为管道,以及文件何时关闭。 还存在一个偏移量指示下一次读或写产生的位置,以及一个末尾指针指示至第三个表的条目,第三个表包含文件索引节点的副本。

文件表包含文件中 opencreate 子例程的每个实例,但是索引节点表只为每个文件包含一个条目。

注: 在处理特殊文件的 opencreat 子例程时, 系统总是调用设备的 open 子例程以允许任何特殊处理 (例如,重新缠绕磁带或打开数据终端就绪的调制解调器导线)。 然而,系统仅在最后一个进程关闭文件时(即取消分配索引节点表条目时)使用 close 子例程。 这意味着设备不能保持或依赖于其用户数量,除非使用专用的设备(防止设备关闭之前被再次打开)。

发生读或写操作时,用户的参数和文件表条目被用于设置下列变量:

  • I/O 目标区域的用户地址
  • 传送的字节计数
  • 文件中的当前位置

如果引用的文件是字符类型的特殊文件,将调用合适的读或写子例程来传送数据,以及更新计数和当前位置。 否则,当前位置被用于计算文件中的逻辑块号。

如果文件是普通文件,逻辑块号必须被映射到物理块号。 无需映射块类型特殊文件。 结果物理块号被用于读或写合适的设备。

块设备驱动程序可以提供用户核心映像和设备之间直接传送信息的能力(块大小根据调用程序请求任意大小,不使用缓冲区)。 方法包括建立对应于裸设备的字符类型特殊文件和提供读写子例程来创建具有合适信息的专用、非共享缓冲区头。 可以提供独立的打开和关闭子例程,并且可以为磁带调用特殊功能的子例程。

流 I/O 接口

流 I/O 接口提供数据作为不由系统解析的字节流,这为网络协议提供比字符 I/O 处理更有效的实现方式。 使用流 I/O 进行读写时,不存在记录边界。 例如,从管道中读取 100 个字节的进程无法确定将数据写入管道的进程是执行了 100 个字节的单次写入,还是执行了 50 个字节的两次写入,或者即使 100 个字节来自两个不同的进程。

流 I/O 可以是管道或 FIFO(先进先出文件)。 FIFO 与管道类似,因为它们允许数据仅以一个方向流动(从左向右)。 然而,与管道不同的是,FIFO 可以被赋予一个名称并且可以由不相关的进程进行访问。 FIFO 有时被称为命名管道。 因为它具有名称,所以可以使用标准 I/O fopen 子例程打开 FIFO。 要打开管道,必须调用 pipe 子例程(该子例程返回一个文件描述符)和标准 I/O fdopen 子例程以将打开的文件描述符与标准 I/O 流相关联。

注: 在用户级别流 I/O 接口缓冲区数据,在执行 fclosefflush 子例程之前无法写入数据,这可能会导致与文件 I/O (例如 read () 或 write ()) 混合时出现意外结果。

通过下列子例程和宏访问流 I/O 接口:

fclose
关闭流
feof, ferror , clearerr 或 fileno
检查流的状态
fflush
写来自流的所有当前缓冲的字符
fopen, freopen 或 fdopen
打开流
fread 或 fwrite
执行二进制输入
fseek, rewind , ftell , fgetpos 或 fsetpos
重新定位流的文件指针
getc, fgetc , getchar 或 getw
从输入流获取字符或单词
get 或 fgets
从流中获取字符串
getwc, fgetwc 或 getwchar
从输入流获取一个宽字符
getws 或 fgetws
从流中获取字符串
printf, fprintf , sprintf , wsprintf , vprintf , vfprintf , vsprintf 或 vwsprintf
显示格式化的输出
putc, putchar , fputc 或 putw
将一个字符或字写入流中
put 或 fput
将一个字符串写入流中
putwc, putwchar 或 fputwc
将一个字符或字写入流中
putws 或 fputws
将一个宽字符字符串写到流中
scanf , fscanf , sscanf 或 wsscanf
转换格式化的输入
setbuf , setvbuf , setbuffer 或 setlinebuf
将缓冲区分配给一个流
ungetc 或 ungetwc
将一个字符推送回输入流中

终端 I/O 接口

终端 I/O 接口在进程和内核之间运行,提供诸如缓冲区和格式化输出等功能。 每个终端和伪终端都具有包含当前进程组标识的 tty 结构。 此字段标识进程组来接收与终端关联的信号。 终端 I/O 接口可以通过 iostat 命令 (它监视 I/O 系统设备装入) 和 udintfd 守护程序 (它允许将内核消息写入系统控制台) 进行访问。

可以通过下列子例程启用或禁用终端特性:

cfgetospeed cfsetospeedcfgetispeed cfsetispeed
获取和设置输入和输出波特率
ioctl
执行与终端关联的控制功能
termdef
查询终端特性
tc漏极
等待输出完成
tcflow
执行流量控制功能
tcflush
从指定的队列丢弃数据
tcgetpgrp
获取前台进程组标识
tcsendbreak
在异步串行数据行上发送一个中断
tcsetattr
设置终端状态
ttylock ttywait ttyunlock ttylocked
控制 tty 锁定功能
ttynameisatty
获取终端的名称
ttyslot
查找当前用户 utmp 文件中的插槽

异步 I/O 接口

异步 I/O 子例程允许进程启动 I/O 操作并在启动或排队操作后立即返回子例程。 还需要另一个子例程来等待操作完成(如果操作已经完成,那么立即返回)。 这意味着进程可以使用其 I/O 覆盖其执行或覆盖不同设备之间的 I/O。 虽然异步 I/O 不能显著提高从磁盘文件读取和写到另一个磁盘文件的进程的性能,但是异步 I/O 可以为其他类型 I/O 驱动的程序(例如将磁盘内容转储到磁带或在图像显示屏上显示图像的程序)提供明显的性能改进。

虽然不需要,在执行异步 I/O 的进程可以告诉内核在指定描述符准备用于 I/O 时(也称为信号驱动的 I/O)通知它。 使用旧的 AIO 时,内核使用 SIGIO 信号通知用户进程。 使用 POSIX AIO 时,程序员使用 sigevent 结构来确定内核的哪个信号用来通知用户进程。 信号包括 SIGIOSIGUSR1SIGUSR2

要使用异步 I/O,进程必须执行下列步骤:

  1. SIGIO 信号建立一个处理程序。 该步骤仅在请求信号通知时必需。
  2. 设置进程标识或进程组标识来接收 SIGIO 信号。 该步骤仅在请求信号通知时必需。
  3. 启用异步 I/O。 系统管理员通常确定是否装入 (启用) 异步 I/O。 启用在系统启动时发生。

提供有下列异步 I/O 子例程:

aio_cancel
取消一个或多个未处理的异步 I/O 请求
aio_error
检索异步 I/O 请求的错误状态
aio_fsync
同步异步的文件。
aio_nwait
暂挂呼叫进程,直到完成特定数目的异步 I/O 请求。
aio_read
从文件描述符进行异步读取
aio_return
检索异步 I/O 请求的返回状态
aio_suspend
暂挂呼叫进程,直到一个或多个异步 I/O 请求完成
aio_write
异步写到文件描述符
lio_listio
使用单个呼叫启动异步 I/O 请求的列表
pollselect
检查多个文件描述符和消息队列的 I/O 状态

为了使用 poll 子例程,提供有下列头文件:

poll.h
定义 poll 子例程使用的结构和标记
aio.h
定义 aio_readaio_writeaio_suspend 子例程使用的结构和标记