入出力処理

このトピックでは、入出力処理のプログラミングに関する考慮事項と入出力 (I/O) 処理サブルーチンを紹介します。

I/O ライブラリー・サブルーチンは、 デバイスまたはファイルのどちらかとの間でデータを送受信することができます。 システムは、デバイスを入出力ファイルであるかのように扱います。 例えば、ファイルの場合と同様に、デバイスのオープンやクローズを行う必要があります。

一部のサブルーチンでは、I/O チャネルとして標準入力と標準出力を使用しますが、 大部分のサブルーチンでは、 データ転送のソースまたは宛先として異なるファイルを指定できます。 一部のサブルーチンでは、ファイルの名前の入った構造体を指すファイル・ポインターを使用でき、 それ以外のサブルーチンではファイル・ディスクリプター (オープン時にファイルに割り当てられた正の整数) を使用することができます。

C ライブラリー (libc.a) に保管されている入出力サブルーチンは、ストリーム入出力を提供します。 これらのストリーム入出力サブルーチンにアクセスするには、以下のステートメントを使用して stdio.h ファイルをインクルードする必要があります。
#include <stdio.h>

この入出力ライブラリー・サブルーチンのいくつかはヘッダー・ファイル内に定義されているマクロで、 いくつかは関数のオブジェクト・モジュールです。 多くの場合、 ライブラリーには同じタイプの操作を行うマクロと関数が入っています。 マクロと関数のどちらを使用するかを決定するときは、以下の点を考慮します。

  • dbx プログラムを使用してマクロのブレークポイントを設定することはできません。
  • マクロはプリプロセッサーによってプログラム内で実際のコード行に置き換えられるので、 マクロは通常それと同等の関数より速くなります。
  • マクロは、コンパイルするとオブジェクト・コードが大きくなります。
  • 関数には、避けなければならない副次作用がある場合があります。

入出力処理に使用されるファイル、コマンド、およびサブルーチンは、 次のインターフェースを提供します。

下位レベル
下位レベル・インターフェースは、ファイルおよびデバイスのための、基本的なオープン関数およびクローズ関数を提供します。
ストリーム
ストリーム・インターフェースは、パイプおよび FIFO のための、読み取りおよび書き込み入出力を提供します。
端末
端末インターフェースは、定様式出力とバッファリングを提供します。
非同期
非同期インターフェースは、入出力と処理の並行処理を提供します。
入力言語
入力言語インターフェースは、lex および yacc コマンドを使用して、入出力を解釈するための字句解析プログラムとパーサー・プログラムを生成します。

下位レベル入出力インターフェース

下位レベルの入出力インターフェースはカーネルへの直接のエントリー・ポイントで、 ファイルのオープン、ファイルの読み書き、およびファイルのクローズなどの関数を提供します。

line コマンドは、標準入力から 1 行を読み取ることができるインターフェースを提供し、以下のサブルーチンは他の低レベル入出力機能を提供します。

open、openx、または creat
ファイルまたは他のパス・オブジェクトを、 割り当てられたファイル・ディスクリプターを用いて読み書きできるように準備します。
read、readx、readv、または readvx
オープン・ファイル・ディスクリプターから読み取ります。
write、writex、writev、または writevx
オープン・ファイル・ディスクリプターへ書き込みます。
閉じる
ファイル・ディスクリプターを解放します。

open サブルーチンと creat サブルーチンは、 これらのシステム・テーブル内の項目をセットアップします。 ファイル・ディスクリプターは最初のテーブルを指し示しており、 このテーブルは読み取りおよび書き込みサブルーチンによってアクセスできるプロセスごとのデータ域としての働きをします。 このテーブルの各エントリーには、2 番目のテーブルの対応するエントリーを指すポインターが入っています。

2 番目のテーブルは、システムごとのデータベース、またはファイル・テーブルで、 オープン・ファイルを複数のプロセス間で共有できるようにします。 このテーブルのエントリーは、そのファイルが読み取り、書き込み、 またはパイプのどの用途でオープンされたか、およびファイルがクローズされた時刻を示しています。 このテーブルには、次の読み取りまたは書き込みが行われる場所を示すオフセットと、 ファイルの i ノードのコピーの入った 3 番目のテーブルへのエントリーを示す最終ポインターもあります。

ファイル・テーブルには、 そのファイルに関する open または create サブルーチンの全インスタンスのエントリーが入っていますが、i ノード・テーブルには 各ファイルごとに 1 つのエントリーしか入っていません。

注: スペシャル・ファイルの open または creat サブルーチンの処理中に、 システムは、常に装置の open サブルーチンを呼び出して、特殊な処理 (テープの巻き戻しやデータ端末作動可能モデム・リード線のオンなど) を可能にします。 ただし、システムが close サブルーチンを使用するのは、 最後のプロセスがファイルをクローズするとき (すなわち、i ノード・テーブル・エントリーが割り当て解除されるとき) だけです。 これは、専用デバイス (クローズしてからでなければ再オープンできないようになっている) が実装されなければ、 デバイスはそのユーザーのカウントを保守できない、 またはあてにならないことを意味します。

読み取りまたは書き込み操作が行われると、ユーザーの引数とファイル・テーブル・エントリーを使用して、以下の変数のセットアップが行われます。

  • 入出力ターゲット領域のユーザー・アドレス
  • 転送に使用するバイト・カウント
  • ファイル内の現在の位置

参照するファイルが文字型スペシャル・ファイルの場合、該当する読み取りまたは書き込みサブルーチンが呼び出されて、データの転送、およびカウントと現在の位置の更新が行われます。 それ以外の場合、現在の位置を使用してファイル内の論理ブロック番号の計算が行われます。

通常のファイルの場合、 論理ブロック番号を物理ブロック番号にマップする必要があります。 ブロック型スペシャル・ファイルの場合は、マップする必要はありません。 この結果得られる物理ブロック番号は、 該当するデバイスの読み取りまたは書き込みに使用されます。

ブロック・デバイス・ドライバーは、ユーザーのコア・イメージとデバイスの間で、呼び出し元の要求するブロック・サイズで、バッファーを使用せずに情報を直接転送する機能を提供することができます。 この方法には、ロー・デバイスに対応する文字型スペシャル・ファイルの設定、 およびプライベートの、 該当する情報を示した非共有バッファー・ヘッダーを作成する読み取りおよび書き込みサブルーチンの提供が関与します。 独立したオープンおよびクローズのサブルーチンを準備し、 磁気テープ用に特殊機能サブルーチンを呼び出すことができます。

ストリーム入出力インターフェース

ストリーム入出力インターフェースは、 システムによる解釈を受けないバイトのストリームの形でデータを提供します。 これは、ネットワーク・プロトコルの場合は文字入出力処理より優れています。 ストリーム入出力を使用して読み書きする場合、レコード境界は存在しません。 例えば、パイプから 100 バイトを読み取るプロセスは、データをパイプに書き込んだプロセスが、 100 バイトの単一書き込みを行ったか、 50 バイトの書き込みを行ったか、または 2 つの異なるプロセスからの 100 バイトがあるかどうかを判別することはできません。

ストリーム入出力は、パイプまたは FIFO (先入れ、先出しファイル) です。 FIFO は、一方向だけ (左から右へ) のデータの流れを可能にするので、 パイプと似ています。 ただし、FIFO には名前を付けることができるので、パイプと違って無関係のプロセスからでもアクセスすることができます。 FIFO は名前付きパイプ と呼ばれることがあります。 名前があるので、FIFO は標準入出力 fopen サブルーチンを使用してオープンできます。 パイプをオープンする場合は、ファイル・ディスクリプターを戻す pipe サブルーチン、 そしてオープン・ファイル・ディスクリプターを標準入出力ストリームと関連付ける fdopen サブルーチンを呼び出す必要があります。

注: ストリーム入出力インターフェースは、ユーザー・レベルでデータをバッファーに入れ、 fclose または fflush サブルーチンが実行されるまでデータを書き込むことができません。これにより、read () または write () などのファイル入出力と混合すると、予期しない結果が生じる可能性があります。

ストリーム入出力インターフェースは、以下のサブルーチンとマクロを使用してアクセスされます。

fclose
ストリームをクローズします。
feof、ferror、clearerr、または fileno
ストリームの状況をチェックします。
fflush
現在バッファーされているすべての文字をストリームから書き出します。
fopen、freopen、または fdopen
ストリームをオープンします。
fread または fwrite
2 進入力を実行します。
fseek、rewind、ftell、fgetpos、または fsetpos
ストリームのファイル・ポインターを位置変更します。
getc、fgetc、getchar、または getw
入力ストリームから文字またはワードを取得します。
gets または fgets
ストリームから文字列を取得します。
getwc、fgetwc、または getwchar
入力ストリームからワイド文字を取得します。
getws または fgetws
ストリームから文字列を取得します。
printf、fprintf、sprintf、wsprintf、vprintf、vfprintf、vsprintf、または vwsprintf
定様式出力を印刷します。
putc、putchar、fputc、または putw
文字またはワードをストリームへ書き込みます。
puts または fputs
ストリームに文字列を書き込みます。
putwc、putwchar、または fputwc
文字またはワードをストリームへ書き込みます。
putws または fputws
ワイド文字列をストリームへ書き込みます。
scanf、fscanf、sscanf、または wsscanf
定様式入力を変換します。
setbuf、setvbuf、setbuffer、または setlinebuf
ストリームにバッファリングを割り当てます。
ungetc または ungetwc
文字をもとの入力ストリームへプッシュします。

端末入出力インターフェース

端末入出力インターフェースは、プロセスとカーネルの間で働いて、 バッファリングや定様式出力などの機能を提供します。 すべての端末および疑似端末には、 現在のプロセス・グループ ID の入った tty 構造体があります。 このフィールドは、その端末と関連するシグナルを受信するプロセス・グループを示しています。 端末入出力インターフェースには、入出力システム・デバイスのロードをモニターする iostat コマンド、およびカーネル・メッセージをシステム・コンソールに書き込むことができる uprintfd デーモンを介してアクセスできます。

端末特性は、以下のサブルーチンを用いて使用可能または使用不可にできます。

cfgetospeed, cfsetospeed, cfgetispeed, または cfsetispeed
入出力ボー・レートを取得して設定します。
ioctl
端末に関連した制御機能を実行します。
termdef
端末特性を照会します。
TCP ドレーン
出力の完了を待ちます。
TCP フロー
フロー制御機能を実行します。
TCP フラッシュ
指定されたキューからデータを廃棄します。
tcgetpgrp
フォアグラウンド・プロセス・グループ ID を取得します。
tcsendbreak
非同期のシリアル・データ回線でブレークを送信します。
tcsetattr
端末の状態を設定します。
ttylock ttywait ttyunlock、または ttylocked
tty ロック機能を制御します。
ttyname または isatty
端末の名前を取得します。
ttyslot
utmp ファイル内の現行ユーザーのスロットを検索します。

非同期入出力インターフェース

プロセスは、非同期通信 I/O サブルーチンを使用すると、入出力操作を開始して、 操作が開始またはキューイングされた後すぐにサブルーチンを戻らせることができます。 他のサブルーチンの場合は、操作が完了するまで待つ (または、 操作が既に終了している場合はすぐに戻る) 必要があります。 これは、プロセスの実行を入出力とオーバーラップさせるか、または 入出力を異なるデバイス間でオーバーラップさせることができることを意味します。 非同期通信 I/O は、ディスク・ファイルから読み取って他のディスク・ファイルに書き込むプロセスの場合は、パフォーマンスはそれほど向上しませんが、他のタイプの入出力主導のプログラム (ディスクから磁気テープへのダンプやイメージ・ディスプレイへのイメージの表示など) では、かなりパフォーマンスが向上します。

必須ではありませんが、非同期通信 I/O を実行するプロセスは、 指定されたディスクリプターが入出力の準備ができたら通知するように、 カーネルに指示することができます (シグナル主導入出力 とも呼ばれます)。 LEGACY AIO を使用する場合、 カーネルは、ユーザー・プロセスに SIGIO シグナルで通知します。 POSIX AIO を使用する場合、 カーネルがユーザー・プロセスへの通知に使用するシグナルを判別するために、 プログラマーは sigevent 構造を使用します。 シグナルには、 SIGIOSIGUSR1、 および SIGUSR2 などがあります。

非同期通信 I/O を使用する場合、プロセスは以下のステップを実行する必要があります。

  1. SIGIO シグナルのハンドラーを確立します。 このステップは、シグナルによる通知を要求する場合にのみ必要です。
  2. プロセス ID またはプロセス・グループ ID を、 SIGIO シグナルを受信するように設定します。 このステップは、シグナルによる通知を要求する場合にのみ必要です。
  3. 非同期入出力を有効にします。 システム管理者は通常、非同期入出力をロード (使用可能)するかどうかを判断します。 使用可能化は、システム始動時に行われます。

以下の非同期通信 I/O サブルーチンが用意されています。

aio_cancel
1 つ以上の未解決の非同期通信 I/O 要求を取り消します。
aio_error
非同期通信 I/O 要求のエラー状況を検索します。
aio_fsync
非同期ファイルを同期化します。
aio_nwait
呼び出し側のプロセスを、 特定の数の非同期通信 I/O 要求が完了するまで延期します。
aio_read
ファイル・ディスクリプターから非同期に読み取ります。
aio_return
非同期通信 I/O 要求の戻り状況を検索します。
aio_suspend
1 つ以上の非同期通信 I/O 要求が完了するまで、呼び出し側のプロセスを延期します。
aio_write
ファイル・ディスクリプターに非同期的に書き込みます。
リオリスティオ (lio_listio)
1 回のコールで非同期通信 I/O 要求のリストを開始します。
poll またはselect
複数のファイル・ディスクリプターとメッセージ・キューの入出力状況をチェックします。

poll サブルーチンで使用するために、以下のヘッダー・ファイルが用意されています。

poll.h
poll サブルーチンで使用する構造体とフラグを定義します。
aio.h
aio_readaio_write、 および aio_suspend の各サブルーチンで使用する構造体とフラグを定義します。