文字ドライバでは、転送は uio (9S) 構造体で記述されます。uio (9S) 構造体には、転送の方向とサイズに関する情報に加え、転送の一方の端のためのバッファーの配列が含まれています。もう一方の端はデバイスです。
uio(9S) 構造体には、次のメンバーが含まれています。
iovec_t *uio_iov; /* base address of the iovec */ /* buffer description array */ int uio_iovcnt; /* the number of iovec structures */ off_t uio_offset; /* 32-bit offset into file where */ /* data is transferred from or to */ offset_t uio_loffset; /* 64-bit offset into file where */ /* data is transferred from or to */ uio_seg_t uio_segflg; /* identifies the type of I/O transfer */ /* UIO_SYSSPACE: kernel <-> kernel */ /* UIO_USERSPACE: kernel <-> user */ short uio_fmode; /* file mode flags (not driver setTable) */ daddr_t uio_limit; /* 32-bit ulimit for file (maximum */ /* block offset). not driver settable. */ diskaddr_t uio_llimit; /* 64-bit ulimit for file (maximum block */ /* block offset). not driver settable. */ int uio_resid; /* amount (in bytes) not */ /* transferred on completion */
uio (9S) 構造体は、ドライバの read(9E) エントリポイントと write (9E) エントリポイントに渡されます。この構造体は、いわゆる gather 書き込みおよびscatter 読み取りをサポートするために一般化されています。デバイスに書き込む場合、書き込まれるデータバッファーがアプリケーションメモリー内で隣接している必要はありません。同様に、デバイスからメモリーに転送されるデータは隣接ストリームで受信されますが、アプリケーションメモリーの不連続領域に格納できます。scatter/gather 入出力の詳細については、readv (2)、writev (2)、pread (2)、および pwrite (2) のマニュアルページを参照してください。
各バッファーは、iovec (9S) 構造体で記述されます。この構造体には、データ領域へのポインタと転送されるバイト数が含まれています。
caddr_t iov_base; /* address of buffer */ int iov_len; /* amount to transfer */
uio 構造体には、 iovec(9S) 構造体の配列へのポインタが含まれています。この配列の基底アドレスは uio_iov 内に保持されており、要素の数は uio_iovcnt 内に格納されています。
uio_offset フィールドには、アプリケーションが転送を開始する必要がある位置として、デバイスへの 32 ビットオフセットが含まれています。uio_loffset は、64 ビットのファイルオフセットのために使用されます。デバイスがオフセットの概念をサポートしていない場合は、これらのフィールドを安全に無視できます。ドライバは、uio_offset と uio_loffset の両方ではなく、このどちらかを解釈する必要があります。ドライバが cb_ops(9S) 構造体内の D_64BIT フラグを設定した場合、そのドライバは uio_loffset を使用します。
uio_resid フィールドは、開始時は転送されるバイト数、つまり、uio_iov 内のすべての iov_len フィールドの合計に設定されます。このフィールドは、戻る前にドライバによって、転送されなかったバイト数に設定される必要があります。 read(2) システムコールと write (2) システムコールは、 read(9E) エントリポイントと write (9E) エントリポイントからの戻り値を使用して、転送の失敗を判定します。失敗が発生した場合、これらのルーチンは -1 を返します。戻り値が成功を示している場合、システムコールは、要求されたバイト数から uio_resid を引いた値を返します。uio_resid がドライバによって変更されない場合、 read(2) と write (2) の呼び出しは 0 を返します。すべてのデータが転送されたにもかかわらず、0 の戻り値によってファイルの終わりが示されます。
サポートルーチン uiomove(9F)、physio (9F)、および aphysio (9F) は、uio (9S) 構造体を直接更新します。これらのサポートルーチンは、データ転送を考慮してデバイスオフセットを更新します。ドライバが、位置の概念を使用するシーク可能なデバイスで使用されている場合は、uio_offset フィールドと uio_loffset フィールドのどちらも調整する必要はありません。この方法でデバイスに対して実行される入出力は、uio_offset または uio_loffset の取り得る最大値によって制約されます。このような使用法の例として、ディスク上の raw 入出力があります。
デバイスに位置の概念がない場合、ドライバは次の手順を実行できます。
uio_offset または uio_loffset を保存します。
入出力操作を実行します。
uio_offset または uio_loffset をフィールドの初期値に復元します。
この方法でデバイスに対して実行される入出力は、uio_offset または uio_loffset の取り得る最大値によって制約されません。このような使用法の例として、シリアル回線上の入出力があります。
次の例は、read(9E) 関数で uio_loffset を保持するための 1 つの方法を示しています。
static int xxread(dev_t dev, struct uio *uio_p, cred_t *cred_p) { offset_t off; /* ... */ off = uio_p->uio_loffset; /* save the offset */ /* do the transfer */ uio_p->uio_loffset = off; /* restore it */ }