在前面我们已经了解到,每个设备都配备了一个设备控制器。当 CPU 向设备控制器发送命令并将其存储在寄存器中时,设备控制器会执行相应的操作。然而,尽管设备控制器会更新状态寄存器的状态,但是如何将这些信息传达给 CPU 呢?
在设备控制器的寄存器中,通常会有一个状态标志位,用于指示输入或输出操作是否完成。因此,我们可以考虑第一种简单的方法,即轮询等待的方式,让 CPU 一直检查寄存器的状态,直到状态标志位被设置为完成。然而,显然这种方式非常低效,它会占用 CPU 的全部时间。
除了轮询等待的方式,还有一种更高效的方法是采用异步通知。这种方式需要一个中间人来进行通知,即中断控制器,它负责向 CPU 发送中断信号。当设备控制器完成操作后,会触发中断信号发送到中断控制器,然后中断控制器通过通知 CPU 来暂停当前的处理逻辑,以处理中断。这种方式能够有效地利用 CPU 的时间,提高系统的响应速度和效率。
中断可以分为软中断和硬中断两种形式。软中断可以由程序或操作系统主动触发,而硬中断则是由硬件通过中断控制器触发的,例如鼠标等外部设备。是由硬件设备发出的信号,通知系统进行相应的处理。中断的使用可以大大提高系统的并发性和响应能力,使得 CPU 能够更加高效地处理多个任务。
中断虽然是一种优化方法,但它也存在一个不足之处,就是会打断 CPU 当前的工作。特别是在需要进行耗时的操作,例如从磁盘读取数据到内存的 IO 操作,CPU 会被占用大量的时间,导致无法同时处理其他任务。对于这种情况,我们可以借助 DMA(直接内存访问)控制器来解决这个问题。
DMA 控制器是在学习 JAVA 零拷贝技术时经常提到的一个概念。它的作用是让设备在没有 CPU 参与的情况下,能够自行将设备的输入/输出数据传输到内存中。要实现 DMA 功能,需要有硬件上的支持,即 DMA 控制器。那么我们来看看 DMA 是如何帮 CPU 解决问题的。
DMA 的工作方式如下:
首先,CPU 向 DMA 控制器发送指令,告知需要读取的内容以及存储数据的内存地址。
接着,DMA 控制器代替 CPU 与磁盘控制器通信,向磁盘控制器发送读取数据的命令。当磁盘控制器将数据缓冲区填满后,它会将数据传输到指定的内存地址。
一旦磁盘控制器完成将数据传输到内存的操作,它会通过总线向 DMA 控制器发送一个确认成功的信号。
当 DMA 控制器接收到确认信号后,它会发送中断通知给 CPU,告知数据传输已经完成。
此时,CPU 可以直接从内存中读取数据,而无需额外的操作。可以看出,在 CPU 需要读取磁盘数据时,只需向 DMA 控制器发送指令,然后 CPU 可以继续执行其他任务。当磁盘数据被拷贝到内存后,DMA 控制器通过中断的方式通知 CPU 数据已经准备就绪,可以直接从内存中读取。因此,除了在传输开始和结束时需要 CPU 的干预外,CPU 的参与度较低。这样可以极大地提高系统的并发性和响应能力。
有人可能会质疑:为什么 DMA 的效率比中断高呢?磁盘控制器完成读取后只需向 CPU 发送一个中断,DMA 完成后也需要发送一个中断,所以它们不是一样的吗?只是 DMA 直接将数据读取到内存中了?
其实中断方式的控制模式是这样的:硬件每准备好一段数据(存放在自己的内部缓冲区中),就向 CPU 发送一个中断信号;CPU 接收到中断信号后,会停止当前的工作,并将硬件缓冲区的每个字符逐个读取到自己的寄存器中,然后将每个字符逐个写入内存。之后,其他应用可以通过系统调用将内存中的数据取出进行进一步处理。
而 DMA 方式是这样的:硬件先准备好若干数据,并将其存放在自己的缓冲区(比如网卡板载的内存芯片)。然后,硬件向 CPU 发送一个中断信号,表示有一定量的数据要提交。CPU 发现硬件支持 DMA,就向硬件发送通知——将数据存放在内存的特定地址范围内,然后让硬件自行处理。
之后,CPU 不再理会这个硬件,而是让硬件通过自己线路板上的简单处理器,逐个字节地将缓冲区中的数据写入指定的内存位置。需要注意的是,在 DMA 方式下,内存的内容是由硬件自己维护的,CPU 不再参与。这样,当硬件自行搬运缓冲区数据时,CPU 可以继续处理自己的任务,提高了系统的并发性和响应能力。
尽管设备控制器屏蔽了设备的诸多细节,但每种设备控制器都具有独特的寄存器、缓冲区等使用模式。因此,为了有效屏蔽设备控制器之间的差异,操作系统引入了设备驱动程序。通过设备驱动程序,操作系统能够统一管理不同设备控制器的操作,使其在各种设备上都能正常工作。
设备控制器作为硬件的一部分,并不属于操作系统的范畴。然而,设备驱动程序则是操作系统的一部分,它提供了一个接口,使得操作系统的内核代码可以像本地调用代码一样使用设备驱动程序。设备驱动程序是专门针对设备控制器编写的代码,它发出指令来操作设备控制器,从而实现对设备的操控。通过设备驱动程序,操作系统能够与设备控制器进行有效的通信,并实现对设备的控制和管理。
尽管不同的设备控制器具有不同的功能,但设备驱动程序会提供一个统一的接口给操作系统,这样不同的设备驱动程序就可以以相同的方式接入操作系统。下图展示:
在前面的讨论中,我们提到了中断的重要性。当设备完成了任务时,它会发送一个中断信号来通知操作系统。操作系统需要有一个地方来处理这个中断,而这个地方就是设备驱动程序。设备驱动程序会及时响应控制器发来的中断请求,并根据中断的类型调用相应的中断处理程序来进行处理。
当设备驱动程序初始化时,通常会注册一个与该设备相关的中断处理函数。中断处理程序的处理流程如下:当设备控制器触发中断并通知中断控制器后,中断控制器会向 CPU 发出中断请求,CPU 会停止当前进程的执行并保存当前进程的上下文。接着,CPU 会调用相应的中断处理函数来处理该中断。中断处理函数完成后,CPU 会恢复之前保存的进程上下文,并继续执行被中断的进程。
在本文中,我们总结了关于 I/O 控制方式的内容。首先,我们介绍了轮询等待和异步通知两种基本的 I/O 控制方式。轮询等待方式效率低下,会占用 CPU 的全部时间,而异步通知方式通过中断控制器来通知 CPU,能够有效提高系统的响应速度和效率。
接着,我们介绍了中断的使用和分类。中断可以分为软中断和硬中断两种形式,它们能够大大提高系统的并发性和响应能力。然而,中断也存在一个问题,即会打断 CPU 当前的工作,导致无法同时处理其他任务。
为了解决这个问题,我们引入了 DMA(直接内存访问)控制器。DMA 控制器能够让设备在没有 CPU 参与的情况下,自行将设备的输入/输出数据传输到内存中,从而减少 CPU 的参与度,提高系统的并发性和响应能力。
最后,我们介绍了设备驱动程序的作用和重要性。设备驱动程序是操作系统的一部分,它提供了一个接口,使得操作系统能够统一管理不同设备控制器的操作。设备驱动程序还负责处理设备触发的中断请求,并调用相应的中断处理程序来进行处理。