本节介绍具有实时进程的 I/O。在 SunOS 中,库提供两组接口和调用,以执行快速异步 I/O 操作。POSIX 异步 I/O 接口是最新的标准。SunOS 环境还提供文件和内存中同步操作和模式,以防止信息丢失和数据不一致。
标准 UNIX I/O 与应用程序编程器同步。调用 read(2) 或 write(2) 的应用程序通常会等待系统调用完成。
实时应用程序需要异步受限的 I/O 行为。发出异步 I/O 调用的进程继续执行,而不等待 I/O 操作完成。当 I/O 操作完成后,会通知调用者。
异步 I/O 可以用于任何 SunOS 文件。文件会异步打开并且不需要进行特殊标记。异步 I/O 传输具有三个元素:调用、请求和操作。应用程序调用异步 I/O 接口,对 I/O 的请求置于队列中,然后调用立即返回。有时,系统会将请求从队列中删除并启动 I/O 操作。
异步和标准 I/O 请求可以在任意文件描述符上混合。系统不维护特定的读取和写入请求序列,而是随意重新排列所有待处理的读取和写入请求。如果应用程序需要特定的序列,则该应用程序必须确保在发出相关请求之前完成前面的操作。
POSIX 异步 I/O 通过 aiocb 结构执行。aiocb 控制块标识每个异步 I/O 请求并包含所有控制信息。控制块一次只能用于一个请求。控制块在其请求完成后可以重复使用。
典型的 POSIX 异步 I/O 操作通过调用 aio_read(3RT) 或 aio_write(3RT) 启动。可以使用轮询或信号确定操作的完成。如果将信号用于完成操作,则每个操作都可以唯一标记。然后标记在生成的信号的 si_value 组件中返回。请参见 siginfo(3HEAD) 手册页。
通过异步 I/O 控制块调用 aio_read(3RT) 以启动读取操作。
通过异步 I/O 控制块调用 aio_write(3RT) 以启动写入操作。
已知操作已完成后,分别调用 aio_return(3RT) 和 aio_error(3RT) 以获取返回值和错误值。
通过异步 I/O 控制块调用 aio_cancel(3RT) 以取消待处理的操作。如果某个特定的请求由控制块指定,则 aio_cancel 可以用于取消该请求。aio_cancel 还可以取消指定文件描述符的所有待处理的请求。
aio_fsync(3RT) 将指定文件上所有待处理的 I/O 操作的异步 fsync(3C) 或 fdatasync(3RT) 请求排入队列。
aio_suspend(3RT) 挂起调用者,仿佛前面的一个或多个异步 I/O 请求是同步发出的一样。
本节讨论 Solaris 操作环境中的异步 I/O 操作。
当异步 I/O 调用成功返回时,I/O 操作只是已排入队列并等待完成。实际操作具有一个返回值和一个潜在的错误标识符。如果调用是同步的,则将此返回值和潜在错误标识符返回给调用者。完成 I/O 时,会将返回值和错误值存储在用户在请求时指定的位置(作为 aio_result_t 的指针)。aio_result_t 的结构在 <sys/asynch.h> 中定义:
typedef struct aio_result_t { ssize_t aio_return; /* return value of read or write */ int aio_errno; /* errno generated by the IO */ } aio_result_t;
更新 aio_result_t 后,会将 SIGIO 信号传送给发出 I/O 请求的进程。
请注意,具有两个或更多待处理的异步 I/O 操作的进程没有特定的方法来确定 SIGIO 信号的原因。接收到 SIGIO 的进程应该检查其可能会生成 SIGIO 信号的所有条件。
aioread(3AIO) 例程是 read(2) 的异步版本。除了常规的读取参数,aioread(3AIO) 还使用指定文件位置和 aio_result_t 结构地址的参数。生成的有关操作的信息存储在 aio_result_t 结构中。文件位置指定在操作之前要在文件内执行的查找。不管 aioread(3AIO) 调用是成功还是失败,都会更新文件指针。
aiowrite(3AIO) 例程是 write(2) 的异步版本。除了常规的写入参数,aiowrite(3AIO) 还使用指定文件位置和 aio_result_t 结构地址的参数。生成的有关操作的信息存储在 aio_result_t 结构中。
文件位置指定在操作之前要在文件内执行的查找操作。如果 aiowrite(3AIO) 调用成功,则文件指针更新为导致成功查找和写入的位置。当写入失败时,文件指针也会更新,以允许后续的写入请求。
aiocancel(3AIO) 例程尝试取消其 aio_result_t 结构指定为参数的异步请求。仅在请求仍然位于队列中时,aiocancel(3AIO) 调用才能成功。如果操作正在进行中,则 aiocancel(3AIO) 会失败。
aiowait(3AIO) 调用会阻塞调用进程,直到至少完成一个未处理的异步 I/O 操作。超时参数指向要等待 I/O 完成的最大间隔。超时值为零指定不想等待。aiowait(3AIO) 返回已完成操作的 aio_result_t 结构的指针。
要同步确定异步 I/O 事件的完成,而不是依赖于 SIGIO 中断,请使用 poll(2)。您也可以轮询以确定 SIGIO 中断的源。
对非常大数量的文件使用 poll(2) 时,其会比较缓慢。此问题可通过 poll(7d) 解决。
使用 /dev/poll 可提供用于轮询大量文件描述符的高度可扩展的方式。此可扩展性通过一组新的 API 和新的驱动程序 /dev/poll 提供。/dev/poll API 是 poll(2) 的备用选项,而非替换选项。使用 poll(7d) 提供 /dev/poll API 的详细信息和示例。如果正确使用,/dev/poll API 的扩展性要比 poll(2) 好得多。此 API 特别适合满足以下条件的应用程序:
重复轮询大量文件描述符的应用程序
轮询的文件描述符相对稳定,意味着描述符不是不断关闭和重新打开
与要轮询的文件描述符总数量相比,实际具有待处理的已轮询事件的文件描述符组很小
通过调用 close(2) 关闭文件。close(2) 调用会取消任何可以关闭的未处理异步 I/O 请求。close(2) 会等待无法取消的操作。有关更多信息,请参见使用 aiocancel。当 close(2) 返回时,针对文件描述符没有待处理的异步 I/O。关闭文件时,仅取消排入指定文件描述符队列的异步 I/O 请求。不会取消其他文件描述符的任何 I/O 待处理请求。
应用程序可能需要确保已将信息写入稳定的存储,或按照特定的顺序执行文件更新。同步的 I/O 可满足这些需求。
在 SunOS 中,如果系统确保所有写入数据在任何后续的文件打开操作后可读取,则写入操作成功。此检查假定物理存储介质没有任何故障。当物理存储介质上的数据映像可用于请求进程时,读取操作的数据传输成功完成。当关联的数据传输成功或操作诊断为不成功时,I/O 操作完成。
在以下情况下,I/O 操作实现了同步 I/O 数据完整性完成:
对于读取,操作已完成或已诊断(如果不成功)。仅当数据映像成功传输到请求进程时,读取才完成。如果在待处理的写入请求影响要读取的数据时请求同步读取操作,则这些写入请求在读取数据之前成功完成。
对于写入,操作已完成或已诊断(如果不成功)。成功传输写入请求中指定的数据时,写入操作成功。此外,必须成功传输检索数据所需的所有文件系统信息。
对于不是数据检索所必需的文件属性,在返回到调用进程之前不进行传输。
同步 I/O 文件完整性完成要求在返回到调用进程之前成功传输所有与 I/O 操作相关的文件属性。否则,同步 I/O 文件完整性完成与同步 I/O 数据完整性完成相同。
fsync(3C) 和 fdatasync(3RT) 将文件显式同步到辅助存储。
fsync(3C) 例程确保在 I/O 文件完整性完成级别同步接口。fdatasync(3RT) 确保在 I/O 数据完整性完成级别同步接口。
应用程序可以在操作完成前同步每个 I/O 操作。使用 open(2) 或 fcntl(2) 在文件描述上设置 O_DSYNC 标志可确保所有 I/O 写入在操作完成之前实现 I/O 数据完成。在文件描述上设置 O_SYNC 标志可确保在将操作指示为已完成之前,所有 I/O 写入都已实现完成。在文件描述上设置 O_RSYNC 标志可确保所有 I/O 读取 read(2) 和 aio_read(3RT) 实现描述符设置请求的相同级别的完成。描述符设置可以是 O_DSYNC或 O_SYNC。