在字符驱动程序中,传输由 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) 入口点。之所以广泛应用此结构,是为了支持称作集中写入和分散读取的操作。向设备写入数据时,待写入的数据缓冲区在应用程序内存中不必是连续的。同样,从设备传输到内存的数据虽然不属于连续流,但也可以写入应用程序内存的非连续区域。有关分散/集中式 I/O 的更多信息,请参见 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 字段都不需要调整。以此方式对设备执行的 I/O 操作受 uio_offset 或 uio_loffset 的最大可能值约束。对磁盘的原始 I/O 操作即是此用法的一个示例。
如果设备没有位置概念,则驱动程序会采取下列步骤:
保存 uio_offset 或 uio_loffset。
执行 I/O 操作。
将 uio_offset 或 uio_loffset 恢复为字段的初始值。
以此方式对设备执行的 I/O 操作不受 uio_offset 或 uio_loffset 的最大可能值约束。此种用法的一个示例是串行线路上的 I/O 操作。
以下示例说明了在 read(9E) 函数中保留 uio_loffset 的一种方法。
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 */ }