6.1 输入和输出
当我们对冯诺伊曼架构进行扩展以支持和外部设备交互时,提到了只能用中断这种方式和它们进行通信。实际上,还有一个功能,输入/输出(I/O)端口,是对中断的一种补充,这种方式允许数据在 CPU 和设备之间进行交换。
- 应用程序有两种方式来访问 I/O 端口:
通过一个独立的 I/O 地址空间。
I/O 端口的地址空间总共有 2^16-1 个地址,从 0 一直到 FFFFH。in 和 out 命令用来在端口和 eax 寄存器(或者 eax 的部分位)之间交换数据。
从端口进行写入和读取的权限检查:
- rflags 寄存器上的 IOPL(I/O 特权级别)位进行控制。
- Task State 段的 I/O 权限位图,这个会在 6.1.1 节中提到
- 通过内存映射进行 I/O: 地址空间的一部分会被映射来提供与外部设备的交互,这样操作外部设备就像直接操作内存中的对象一样。进而对内存地址的一些操作指令(mov,movsb 等等)就可以被用来进行设备的 I/O 操作了。 这种 I/O 任务执行时,标准的分段和页保护机制也是生效的。
rflags 寄存器上的 IOPL 字段按照如下方式工作:如果当前的特权级别小于或者等于 IOPL,那么下面的指令允许被执行:
- in 和 out(正常的输入/输出)
- ins 和 outs(string 输入/输出)
- cli 和 sti(clear/set 中断 flag)
因此在应用中独立地设置 IOPL 使我们能够禁止其被写入,哪怕是在比用户应用更高的特权模式下也不行。
另外,Intel 64 允许一种更好的权限控制仪式,通过 I/O 权限位图来进行控制。如果 IOPL 检查通过了的话,处理器会检查位对应的使用过的端口。只有在这一位没有被设置的情况下,操作才可以继续向前执行。
I/O 权限位图是 Task State 段的一部分,TSS(Task State 段)是每个进程独立享有的。不过因为硬件的任务切换机制已经废弃了,所以在长模式下只有一个 TSS(以及 I/O 权限位图)可以同时存在。