CPU和I/O设备交互——一分钟系列
小贴士:小孩子尝试一下在很少的时间里讲清一个知识点。
前情回顾
通过阅读上一篇文章《编程到底是在编什么》,我们应该知道了:
•编程
的本质就是在写机器指令,而机器指令的本质就是一连串的二进制位。
•指令是被存储到内存中的,CPU会从内存中逐条将指令取出并执行。
•指令主要完成下边4个任务:
1.load任务:即将内存的数据加载到CPU的寄存器中2.store任务:即将CPU的寄存器中的数据写入到内存中3.jump任务:即不必按顺序执行指令,而是跳转到指定的指令执行4.operate任务:即将寄存器数据、内存数据或者立即数进行算术逻辑运算
面临的问题
现在面临两个重要的问题:
•问题一、数据被存储到CPU的寄存器或者内存的存储单元,人类无法用眼睛看到,所以程序运行过程中都发生了什么事我们无法知道,程序运行的结果我们也无法知道。我们需要某个设备来展示程序在运行过程中我们想要看到的那些数据。
•问题二:程序运行过程中所需要的数据可能并不是在编写程序前就准备好的,而是在程序运行过程中动态输入的(比方说在游戏程序中需要玩家手动向程序提供一些数据)。
为了解决问题一
,我们需要引入一些被称作输出设备
(Input Device)的东西,诸如显示器、打印机什么的。为了解决问题二
,我们需要引入一些被称作输出入备
(Output Device)的东西,诸如键盘、鼠标、触摸板、麦克风、摄像头什么的。
像硬盘、U盘这样的大容量存储设备可以既作为输入设备,也作为输出设备。
CPU和I/O设备的通信
I/O设备种类多种多样,具体的运行原理也各不相同,但对于CPU来说,只需要通过找中介的方式来和这些I/O设备打交道,这里所说的“中介”指的是各种I/O设备的控制器。
各种I/O设备的控制器里提供了一些寄存器,这些寄存器大致可以分为:
•状态寄存器,状态寄存器中保存着该I/O设备的状态信息,通常是只读的。比方说我们想从硬盘读数据的时候,等硬盘准备好数据后,硬盘控制器中的状态寄存器中的某个位就会被置1,然后CPU通过读取状态寄存器的值就可以知道硬盘准备好数据了。
•控制寄存器,CPU通过修改控制寄存器的值来控制I/O设备的行为,通常是只写的。I/O设备通常会向CPU提供若干种功能,CPU向控制寄存器中写入不同的值代表不同的命令,让I/O设备完成不同的功能。比方说CPU可以让硬盘控制器读取数据或者写入数据。• 地址寄存器,通常是只写的。CPU将要从I/O设备中读取的数据所在地址发送到该寄存器。
•数据寄存器,通常是可读可写的。CPU可以从该寄存器中读出从输入设备中返回的数据或者将写入输出设备的数据写入该寄存器。
不同的I/O设备视复杂程度不同,相应的控制器中提供数量不等的寄存器与CPU沟通。
站在软件程序员的角度上,I/O设备控制器提供的寄存器也被称作I/O端口
(I/O port),不同的CPU给软件程序员提供了两种不同的访问I/O端口的方式:
•内存映射式I/O
•专用访问I/O端口指令
内存映射式I/O
话说CPU是通过给出地址来访问内存中的数据的,其实这些地址可以并不完全用于访问内存的存储单元,也可以拿出一小部分CPU地址来访问I/O端口
这样对于同样的load、store指令,具体访问的是内存的存储单元还是I/O端口,就取决于指令中给出地址是什么,我们通过这种将CPU地址映射到I/O端口,从而通过复用原先的load、store指令来访问I/O端口的方式称作内存映射式I/O
(Memory Mapped I/O)。
专用访问I/O端口指令
有的CPU将它可能访问的I/O端口统一编号,这些编号统称为I/O地址空间。然后通过专门提供访问I/O端口的指令来访问这些端口的数据。Intel处理器提供了:
•IN指令,用于从I/O端口中读取数据到CPU寄存器中•OUT指令,用于将CPU寄存器中的数据写入到I/O端口中
内存映射式I/O减少了程序员学习新指令的成本,但是却要牺牲一部分CPU地址来用于访问I/O设备;专用访问I/O端口指令虽然需要学习新指令,但不会占用CPU访问内存的地址。像Intel处理器对某些特别重要的I/O设备采用内存映射式I/O,访问另一些I/O设备则需采用专用访问I/O端口指令。