906-直接存储器访问方式
# 906-直接存储器访问方式
我们还是来说快递的事。 有一天,你在这坐着运算,突然来电话了, 然后你接了一听,啊, 又有快递啦,啊什么?一千本书, 现在就得去拿,哎呀,好好好。 这个怎么办呢?这个,这搬一千本书又得浪费很多时间, 呃,好办,啊那我们就叫一个搬家公司, 跟他们说好,我们要从哪搬到哪谈好价钱,他们就去搬, 并且告诉他们搬完之后呐给我来一个电话,然后我去检查,这就可以了。 那么,虽然要多花一些钱,但是如果能够运算更重要的任务, 那还是值得的。这就是我们要说的DMA方式。
现在的计算机中有很多复杂的外设比如像显示器,网络,硬盘, 这些外设需要传输的数据量很大,而且对传输的速率也有很高的要求, 如果这些数据都要靠CPU一个一个来搬运的话,那恐怕就难以应对了,所以这就需要用到DMA这种IO控制方式。
DMA就是直接存储器访问的简称。 那如果采用DMA方式进行I/O数据的传送在传送的过程中是不需要CPU干预的,这个数据传送的工作是由一个专门的硬件电路控制**,可以直接将外设的数据传到存储器或者将存储器中的数据传到外设****, 而这个专门的硬件控制电路就称为DMA控制器,简称为DMAC, 其实DMA控制器本身也是一个I/O口**, 和其他I/O接口类似,它早期也是采用独立芯片的形式, 而现在通常是寄存在其他多功能的芯片当中。
那我们来看DMA控制器的基本工作步骤。 这是一个简化的系统,里面有一个CPU,一个存储器, 一个I/O接口,还有一个DMA控制器,它们通过系统总线连接在一起, 这里还(给部件)增加了m和s这两种标记,m是master的缩写, 表示这个部件可以在系统总线上主动发起传输, 比如CPU就是这样的部件,它可以在系统总线上主动发起读写的传输。 而s是slave的缩写,它表示这个部件只能被动地接受来自系统总线的传输。 那存储器就是一个典型的只有slave接口的设备。 一般的I/O接口也是这样,只会接受来自CPU的访问, 而DMA控制器则是既有master接口又有slave接口。
那我们就要用这个DMA控制器进行一次外设到内存的传送, 我们可以把这个I/O接口看成是网卡,外面接着网线, 现在我们需要用DMA的方式接收一个网络包,并保存到存储器的某个区域, 那要完成这个操作,CPU首先需要设置DMA内部的配置寄存器,那对于x86CPU,我们就要用out指令续写DMA 控制器当中的一些I/O端口,以配置好它的工作模式, 然后这个DMA控制器就处于空闲等待状态,而CPU也可以去执行其他的程序了。
那当外设送来数据到I/O接口的时候, I/O接口就会像DMA控制器发出DMA传送的申请, 这个申请需要通过一根额外的连线发出, 那DMA控制器收到I/O接口的申请之后,还会通过另一个连线响应这个申请, 然后DMA控制器就会通过它的master接口发起总线读传输, 而这个读传输的地址就是这个I/O接口当中的数据输入缓冲寄存器。 那这样数据就会从I/O接口被读到了DMA控置器当中,然后DMA控制器会向存储器发起总线的写传输,将刚才读回的这个数据写到存储器的某个区域。
我们注意在有DMA控制器之前, 这个系统当中只有CPU可以发起总线传输, 而现在DMA控制器可以主动发起总线传输了。 那接下来DMA控制器会重复五和六这两个动作, 不断地从I/O接口中读出数据再写到存储器当中去, 那如果这时网络传输一直到收完一个网络包, 就次DMA传送才算完成,然后DMA控制器会返回到第二步,等待I/O发起下一次DMA传送的申请。
那一次DMA传送的数据可能很多, 所花的时间也很长,但是在这段时间CPU一直可以在执行其他的程序, 这样就和数据传送的工作并行起来,可以获得很好的系统性能。 但是CPU怎么知道DMA传送已经完成了呢?那通常情况下DMA传送完成后,DMA控制器会发出一个中断请求信号,通过中断控制器通知CPU, 那这个DMA控制器发出的中断,也是一个外部中断,后面的处理过程就和其他I/O接口发出的中断是一样的, 只不过它对应的中断服务程序是让CPU来对这一次的DMA传输进行处理。
所以从这个步骤我们可以看出 DMA方式也不是完全不用CPU来干预,在DMA启动的时候CPU来进行配置 而传送完成后CPU还需要来进行处理。 那后续的处理根据任务的需要各有不同,而初始化的配置却大体是一样的。
在DMA传送开始前CPU要设置DMA控制器内部的寄存器, 一般至少要设置这么几项,既然等一会儿DMA控制器要进行数据的传送, 那就需要先设置好从哪里开始传,这也就是源地址的初始值。 还需要设置在传送的过程中这个源地址是递增还是递减,然后还需要 设置这些数据传输到哪里,也就是目的地址的初始值以及传送时的地址增减方式。 最后还要设置需要传送多少数据。
那么我们还是以刚才提到的外设到内存的传送为例。 那CPU在初始化配置时,会将某个源地址设为I/O端口, 比如就是一个网络控制器的数据输入寄存器端口, 而且传送时这个源地址是不变的。 因为每一次DMA控制器都是从同一个I/O端口读出数据,
而目的地址要设置为存储器的某个地址, 而且传送时是递增的,这样DMA控制器每次从I/O接口当中读出一个数就把这个数写到存储器当中去, 下一次再读出一个数再写到存储器当中时地址就应该递增, 这样才不会覆盖刚才传过来的这个数。
第三要设置待传送数据的长度,那如果是从外设接收数据, CPU在配置时可能不知道究竟这个数据有多长, 那就可以不设置这个参数。
最后根据I/O接口的控制信号来判断是否传输完成。
那如果是从存储器发送一组数据到I/O接口, 这个时候CPU在初始化时就是知道究竟要发送多长的数据。 那就需要设置这个待传送数据的长度的这个参数。
那需要强调的是, 这些参数都是DMA控制器内部的寄存器,一般各自都有一个I/O端口的地址, 那么编程时通过out指令让CPU去写这些I/O端口,从而配置好了源地址,目的地址和待传送数据的长度等信息,DMA控制器在运行的过程中就不需要再靠CPU 执行程序来控制,而是直接查看内部的源地址寄存器,就把对应的地址发到系统总线上, 就从I/O接口读回了相应的数据,然后再查看自己的目的地址寄存器, 把这个地址和刚才读回来的数据一起发到系统总线上,这样就写入到了存储器当中去。 而每读写一次, 就在内部累计已经传送的数据的长度,并和这个待传送数据的长度的寄存器的内容进行比较, 如果相等则意味着传输已经完成,如果还不等则继续传输。 所以CPU在一次配置完之后,后续的工作都由DMA控制器的内部硬件自动完成, 不再需要CPU的干预了。这就是所谓直接存储器访问的含义。
# 独立的DMA控制器
那在最早的个人计算机当中是没有DMA控制器的, 那后来为了提高输入输出的效率,就增加了独立的DMA控制器的芯片。例如刚才提到的8237, 那通过CPU设置DMA控制器当中的不同的地址就可以为不同的I/O接口提供DMA的服务。
但是随着计算机的发展有一些I/O接口的速度越来越快, 对DMA传输的要求也越来越高。 那多个I/O接口共享一个独立的DMA控制器的方式可能就没有办法满足部分I/O接口的需求了。
这时就出现了自带DMA控制器的I/O接口, 那这样的I/O接口内部带有一个专属的DMA控制器,只为这个I/O接口提供服务,那这个I/O接口现在也有了master的总线接口。 那在显卡、网卡、硬盘控制器这些对传送率要求很高的I/O接口中一般都会自带DMA控制器, 那在系统初始化时,CPU要配置好各个DMA控制器, 然后外设有传输需求时这些DMA控制器就可以自动地开始工作了。
那如果我们把这个独立的DMA控制器比作一个搬家公司, CPU就请这个搬家公司来完成I/O接口和存储器之间的数据搬运工作。 而有些部门的搬运工作量非常大, 实际上需要一个搬家公司全时地为他们服务才能够满足需求。 于是他就在部门内部自己组建了一个搬运队。 那这样一旦有数据传送的需求就可以马上开始工作,而不需要去申请外部的这个DMA控制器了,传输的效率自然会大大提升。
而且不同的I/O接口有不同的传输的特点,比如显示、网络、硬盘传输的行为肯定有不同的特征,而内线的DMA控制器就可以根据I/O接口的特点进行定制,从而更加高效地完成传输。
那在现代的计算系统当中, 大部分对数据传输率有比较高要求的设备都会自带DMA控制器, 而其他对数据传输率要求比较低的设备则可以共享系统中独立的DMA控制器。 另外这个独立的DMA控制器一般还会提供从内存到内存传送的服务。 那当我们编程时需要将内存中的一大块数据复制到内存的另一个区域的时候, 虽然不涉及输入输出,但是也可以享受到DMA带来的好处。 那当然也不是所有的输入输出设备都需要使用DMA的方式。 毕竟增加一个DMA控制器需要增加制造的成本, 而且CPU来配置DMA控制器以及进行后续的处理还是要靠执行程序来完成的,也都需要花时间。 如果要传输的数据量很小,性能反而会变差了。
那现在用DMA方式的这些设备不见得是这些工作CPU不愿意干,有可能是根本干不了。 比如说我们快递要送来的不是一千本书而是一万本书,而且非得一个小时内办完, 那你能干得了吗?非得叫搬家公司不可了,对不对? 但是不是说所有的事情都用DMA方式都能好好的解决的。 比如送来的不是一千本一万本书,而就是一本书, 那你也去叫一个搬家公司把这本书给你运来?那样就得不偿失了。 而且有时候你可能送来的根本不是书, 而是你的午饭,对吧,订了一份盒饭,难道你也叫一个搬家公司来给你收这个盒饭?那他们可能过了两个小时才来, 然后用他们规范的方法把你这顿饭给打包装箱送到他们的大货车, 然后运到你的楼下,再拆包,再给你送上来, 能赶上吃晚饭就不错了。