1. 从一道408真题说起:DMA到底在考什么
如果你正在准备计算机408统考,或者正在啃唐朔飞、白中英那几本计算机组成原理的教材,那你一定绕不开一个知识点——DMA方式。这个知识点几乎年年出现在选择题甚至大题里,比如24年45题就考了DMA相关的计算与概念辨析。很多同学第一次学到这里的时候,脑子里全是问号:为什么有了中断方式还不够?DMA控制器到底是怎么"绕过CPU"搬运数据的?周期挪用和停止CPU访存这两种方式到底有什么区别?
我当年复习408的时候,DMA这一块也是反复看了好几遍才真正理清楚。教材上讲得比较抽象,王道和各类试题库里的题目又往往只给结论不给推导过程,导致很多人是"背下来的"而不是"理解了的"。但这恰恰是408最容易失分的地方——因为DMA的考点非常细,选择题可以考你三种DMA传送方式的效率对比,大题可以考你DMA传送时间的计算,甚至还能和总线、存储器、中断系统串起来综合出题。
所以这篇内容,我想从一个过来人的角度,把I/O设备与主机之间信息传送的控制方式这条主线彻底捋清楚,重点把DMA方式的原理、三种实现方式、与中断方式的本质区别、以及考试中常见的计算套路讲透。不管你是刚开始学组成原理的小白,还是已经刷了几套题但总觉得DMA这块不踏实的考研党,看完之后应该都能有一个清晰的框架。我会尽量用生活化的类比来解释那些抽象概念,同时把考试里真正会考的细节一个不落地补上。
2. 为什么程序查询和中断方式都不够用
2.1 程序查询方式:CPU被彻底绑死
要理解DMA为什么会出现,得先看看它前面的两种方式有什么问题。最早的信息传送方式是程序查询方式,也叫程序轮询。它的逻辑特别简单粗暴:CPU不断读取I/O设备的状态寄存器,看看设备准备好了没有,如果准备好了就传送一个数据,没准备好就继续循环等待。
你可以把它想象成你去一家很火的餐厅吃饭,但是这家餐厅没有取号系统,你只能站在柜台前一遍遍问服务员"好了吗?好了吗?"。在等待的这段时间里,你什么别的事都干不了。对应到计算机里就是:CPU在轮询期间完全被这个I/O操作占住了,不能执行其他程序。对于慢速的I/O设备(比如键盘),CPU可能要轮询成千上万次才能等到一个字符输入,效率低得令人发指。
这种方式唯一的优点是硬件结构简单,不需要额外的控制电路。所以在一些极其简单的嵌入式场景里偶尔还能见到,但在现代计算机系统中基本已经被淘汰了。
2.2 中断方式:CPU解放了,但数据搬运还是它干
中断方式的出现是一次巨大的进步。它的核心思想是:CPU不需要主动去问设备"你好了没",而是设备准备好之后主动给CPU发一个中断信号,CPU收到信号后再暂停当前程序,转去执行中断服务程序来处理数据传送。
还是用餐厅的类比:现在餐厅有了取号系统,你拿个号就可以去逛街了,等叫到你的号再回来。这期间你的时间被解放出来了,可以干别的事。对应到计算机里就是:CPU在等待I/O设备准备数据的过程中,可以继续执行其他程序,只有真正需要传送数据的时候才被打断。
但是,中断方式有一个容易被忽略的"隐藏成本":每一次数据传送都需要CPU亲自参与。具体来说,当设备准备好一个数据后,它发中断给CPU,CPU需要保存现场、跳转到中断服务程序、执行数据传送指令(从设备寄存器读到内存或者反过来)、恢复现场、返回原程序。这一套流程下来,CPU花在"搬运"上的开销其实非常大。
对于高速设备(比如磁盘、网卡),数据传送非常频繁,如果每传送一个字都要走一遍完整的中断流程,CPU大部分时间都在做搬运工,真正用于计算的时间被严重挤压。这就好比你虽然可以逛街了,但每次叫号你都得亲自跑回餐厅端一次菜,如果菜很多,你跑来跑去的次数多了,逛街也逛不安生。
2.3 瓶颈的本质:数据搬运必须经过CPU
程序查询和中断方式有一个共同的本质特征:数据传送必须经过CPU的寄存器。也就是说,数据从I/O设备到主存的路径是"I/O设备 → CPU寄存器 → 主存",反过来也一样。CPU在这里扮演了一个"中转站"的角色。
这个瓶颈在低速设备场景下还能忍,但到了高速大批量数据传输的场景就完全不行了。于是,一种新的思路出现了:能不能让数据不经过CPU,直接在I/O设备和主存之间开辟一条通道?这就是**DMA(Direct Memory Access,直接存储器访问)**方式的核心思想。
3. DMA控制器的内部结构与工作流程
3.1 DMA控制器的核心组成部件
DMA方式之所以能让数据"绕过CPU",关键在于系统中增加了一个专门的硬件部件——DMA控制器(DMAC)。这个控制器本质上是一个能接管总线控制权的小型处理器,它内部有几个关键的寄存器,理解了这几个寄存器,就理解了DMA的工作机制。
首先是内存地址寄存器(AR),它存放的是本次DMA传送在主存中的起始地址。每传送一个数据,这个地址会自动加一(或减一),指向下一个存储单元。其次是字计数器(WC),它记录还需要传送多少个数据,每传送一个就减一,减到零时表示传送完毕。这两个寄存器决定了"数据传到哪里"和"传多少"。
然后是数据缓冲寄存器,用于暂时存放正在传送的数据。还有设备地址寄存器或者叫设备端口寄存器,用来指定与哪个I/O设备进行数据交换。最后是控制与状态寄存器,里面存放着传送方向(是读还是写)、是否启动DMA、传送是否完成等控制信息和状态信息。
除了这些寄存器,DMA控制器还需要一套中断机构。注意,DMA传送结束后,DMA控制器会向CPU发一个中断信号,告诉CPU"这批数据我搬完了"。所以DMA方式并不是完全不用中断,而是只在整批数据传送完成后才中断一次,而不是每传一个数据就中断一次。这个区别非常关键,也是考试常考的点。
3.2 DMA传送的完整流程拆解
我把DMA的一次完整传送过程拆成几个阶段来讲,这样你脑子里会有一个清晰的时间线。
第一阶段是预处理阶段。CPU在执行主程序的过程中,发现需要启动一次DMA传送(比如要从磁盘读取一个文件块到内存),于是CPU通过程序设置DMA控制器的各个寄存器:把主存起始地址写入AR,把要传送的数据个数写入WC,把设备地址写入设备寄存器,把传送方向和控制信息写入控制寄存器。设置完成后,CPU启动DMA控制器,然后就可以去执行其他程序了。
第二阶段是数据传送阶段。DMA控制器被启动后,开始向CPU发出总线请求信号(HRQ)。CPU在当前总线周期结束后,如果同意让出总线,就发出总线响应信号(HLDA)。DMA控制器收到响应后,就接管了总线的控制权,开始直接在I/O设备和主存之间传送数据。每传送一个数据,AR加一,WC减一。这个过程完全由硬件控制,不需要CPU干预。
第三阶段是结束处理阶段。当WC减到零时,DMA控制器知道这批数据传完了,于是释放总线控制权,同时向CPU发出中断请求。CPU响应中断后,执行一小段中断服务程序,做一些收尾工作,比如检查传送是否有错误、设置标志位表示数据可用等。
整个流程中,CPU真正参与的只有最开始设置寄存器和最后处理中断这两小段,中间大批量的数据搬运完全由DMA控制器硬件完成。这就是DMA效率高的根本原因。
3.3 总线控制权的交接:DMA和CPU怎么"抢"总线
这里有一个很多同学容易迷糊的点:DMA控制器接管总线的时候,CPU在干什么?答案是CPU被"暂停"了对总线的使用。因为总线是共享资源,同一时刻只能有一个主设备控制总线。当DMA控制器成为总线主设备时,CPU就不能访问主存了。
但注意,CPU不一定完全停止工作。如果CPU当前执行的指令和数据都在Cache里,那么即使不访问主存,CPU也能继续执行一段时间。这就是为什么现代计算机中Cache的存在能进一步提高DMA的效率——CPU和DMA控制器可以并行工作,一个在Cache里跑程序,一个在主存和I/O之间搬数据。
不过从408考试的角度,通常简化处理为:DMA传送期间CPU不能访存。这个简化假设在做计算题的时候非常重要,后面讲计算的时候会详细说。
4. 三种DMA传送方式:停止CPU访存、周期挪用与交替访问
4.1 停止CPU访存方式:简单但浪费
停止CPU访存方式是最简单的一种DMA实现。当DMA控制器需要传送数据时,它向CPU发出总线请求,CPU响应后完全放弃总线的控制权,直到整批数据全部传送完毕才把总线还给CPU。
这种方式的好处是控制简单,DMA控制器不需要复杂的时序逻辑。但缺点也很明显:在DMA传送期间,CPU要么完全停止访存(如果Cache未命中就只能干等),要么只能依靠Cache运行。如果传送的数据量很大,CPU被"冻结"的时间就会很长,对系统性能影响很大。
打个比方:这就像你要搬一批家具进房间,你把房间门完全锁上,不让任何人进出,直到所有家具搬完才开门。虽然搬家具的过程很顺畅,但其他人这段时间完全没法用这个房间。
这种方式适合数据传输率极高的设备,因为如果设备传输率极高,CPU就算想穿插访存也插不进去多少,不如干脆让DMA一次传完。
4.2 周期挪用方式:DMA"偷"一个周期
周期挪用方式也叫周期窃取方式,是实际系统中最常用的一种。它的核心思想是:DMA控制器不需要一直霸占总线,而是在需要传送一个数据的时候,向CPU"借"一个总线周期(或者叫挪用、窃取一个存储周期),传完这个数据就把总线还给CPU。
具体来说,当DMA控制器准备好一个数据要传送时,它向CPU发出总线请求。CPU在当前总线周期结束后响应请求,让出一个总线周期给DMA使用。DMA在这个周期内完成一个数据的传送,然后释放总线。CPU继续执行程序,直到DMA下一次需要传送数据时再重复这个过程。
这种方式的好处是CPU和DMA可以交替使用总线,CPU不会长时间被阻塞。但代价是DMA控制器的控制逻辑更复杂,而且每次传送都要经历一次总线请求和响应的过程,有一定的开销。
用搬家具的类比:这次你不是把门锁上了,而是每次搬一件家具的时候跟房间里的人说"借过一下",搬完一件就让人家继续用房间。这样房间里的人虽然会被打断几次,但不会长时间没法用。
这里有一个考试常考的计算点:在周期挪用方式下,DMA传送一个数据需要占用一个存储周期。如果主存的存取周期为T,DMA需要传送n个数据,那么DMA总共占用的存储周期数是n,CPU被"偷走"的时间就是n×T。但注意,DMA传送数据本身也需要时间,如果I/O设备的数据传输率很高,DMA请求非常频繁,那么CPU被挪用的周期比例就会很高,甚至可能影响CPU的正常执行。
4.3 交替访问方式:把时间切片分给双方
交替访问方式也叫透明DMA方式,它的思路又不一样。这种方式下,不需要DMA发出总线请求,而是把存储周期分成两个时间片,一个给CPU用,一个给DMA用。比如存储周期为T,前半个周期T/2给CPU访存,后半个周期T/2给DMA传送数据。两者交替使用,互不干扰。
这种方式的好处是不需要总线请求和响应的握手过程,时序控制简单,CPU和DMA都能获得固定的访存时间。但缺点是对主存的存取速度要求更高,因为存储周期被分成了两半,相当于要求主存的速度翻倍。如果主存本身速度不够快,这种方式就无法实现。
这种方式适合高速外设,因为DMA有固定的时间片可用,不需要等待总线请求的响应。但由于对主存速度要求高,实际系统中用得不如周期挪用方式普遍。
4.4 三种方式的对比与考点总结
我把三种方式的关键特征整理成一张表,方便你对比记忆:
| 对比维度 | 停止CPU访存 | 周期挪用 | 交替访问 |
|---|---|---|---|
| 总线控制方式 | DMA独占 | DMA与CPU交替 | 时间片固定分配 |
| CPU受影响程度 | 最大(可能完全停止访存) | 较小(被挪用个别周期) | 固定(每周期让出一半) |
| DMA控制复杂度 | 最简单 | 较复杂 | 中等 |
| 对主存速度要求 | 无特殊要求 | 无特殊要求 | 要求较高 |
| 适用场景 | 高速大批量传送 | 通用场景 | 高速外设 |
| 是否需要总线请求 | 需要 | 需要 | 不需要 |
考试中最常考的是周期挪用方式,因为它最典型也最实用。你需要记住的核心结论是:周期挪用方式下,DMA每传送一个数据占用一个存储周期,CPU被挪用的时间等于DMA传送的数据个数乘以存储周期。
5. DMA方式与中断方式的本质区别
5.1 从"谁在搬数据"看本质差异
很多同学学完DMA之后,还是分不清DMA和中断到底有什么本质区别。我用一句话来概括:中断方式是CPU亲自搬数据,DMA方式是DMA控制器替CPU搬数据。
在中断方式下,数据传送的路径是"I/O设备 → CPU寄存器 → 主存",CPU必须执行指令来完成这个搬运。在DMA方式下,数据传送的路径是"I/O设备 → 主存"(或反向),CPU不参与具体的数据搬运,只负责最开始设置参数和最后处理中断。
这个本质差异带来了一系列连锁反应。因为CPU不参与搬运,所以DMA方式下不需要保存和恢复现场(那是中断才需要的),也不需要执行指令,传送速度可以非常快。也因为CPU不参与搬运,所以DMA方式下数据不经过CPU的寄存器,这对CPU的寄存器资源也是一种节省。
5.2 中断时机与响应方式的差异
中断方式是每传送一个数据就中断一次,CPU需要频繁地响应中断、保存现场、恢复现场。DMA方式是整批数据传送完成后才中断一次,中断频率大大降低。
这个差异在考试中经常以对比题的形式出现。比如题目会问:"DMA方式与中断方式相比,主要优点是什么?"标准答案是:DMA方式在数据传送过程中不需要CPU干预,只在传送结束后才中断CPU,因此CPU开销小,适合高速大批量数据传送。
还有一个容易混淆的点:DMA请求和中断请求是两种不同的请求。DMA请求的是总线控制权,中断请求的是CPU的处理时间。DMA请求的优先级通常高于中断请求,因为总线控制权如果被中断抢走了,DMA传送就会中断,可能导致数据丢失。所以在优先级排序上,通常是:DMA请求 > 中断请求 > 程序查询。
5.3 响应时机的差异
中断方式下,CPU通常在一条指令执行完毕后才响应中断请求。而DMA请求可以在一个总线周期结束后就响应,响应粒度更细。这也是DMA方式效率更高的一个原因——它不需要等到一条指令执行完,只要当前总线周期结束就可以让出总线。
不过这里要注意,DMA请求虽然可以在总线周期结束后响应,但CPU当前正在执行的总线周期是不能被打断的。也就是说,如果CPU正在进行一次访存操作,DMA必须等这次访存完成才能接管总线。这个细节在做时序分析题的时候可能会用到。
6. DMA传送时间的计算套路与真题拆解
6.1 周期挪用方式下的时间计算
这是408考试中最常考的一类计算题。我先把基本公式说清楚,然后用一个具体例子来演示。
在周期挪用方式下,假设主存的存取周期为T(单位通常是ns),DMA需要传送n个数据,那么DMA占用总线的时间就是n×T。但这里有一个关键问题:DMA传送数据的速度受限于I/O设备的数据传输率。如果I/O设备每传送一个数据需要的时间是t,那么DMA两次请求之间的间隔至少是t。如果t > T,那么DMA不会连续占用总线,CPU有足够的时间在两次DMA请求之间执行程序。如果t < T,那么DMA请求会非常频繁,CPU可能来不及执行多少指令就被再次挪用周期。
考试中常见的问法是:给定主存存取周期、I/O设备传输率、数据块大小,求DMA传送占用的时间比例,或者求CPU被挪用周期占总周期的比例。
举个例子:主存存取周期为500ns,I/O设备的数据传输率为2MB/s,要传送一个4KB的数据块。首先计算I/O设备传送一个字节需要的时间:1s / 2MB = 1s / (2×10^6 B) = 500ns。也就是说,I/O设备每500ns准备好一个字节。而主存存取周期也是500ns,所以DMA每500ns挪用一次总线,每次挪用500ns。这意味着总线几乎被DMA和CPU各占一半。传送4KB数据需要挪用4096次,总时间约为4096×500ns = 2.048ms。
6.2 停止CPU访存方式下的时间计算
停止CPU访存方式的计算更简单:DMA从开始传送到结束,CPU完全不能访存。传送n个数据的时间取决于I/O设备的速度和主存的速度中较慢的那个。如果I/O设备每t时间准备好一个数据,主存每T时间能完成一次存取,那么传送一个数据的实际时间大约是max(t, T)。总时间就是n×max(t, T)。
但考试中通常简化处理,直接认为DMA传送n个数据占用n个存储周期,即n×T。因为在这种方式下,DMA一旦获得总线就连续传送,不需要反复请求。
6.3 一道典型真题的完整拆解
我拿一道经典的408风格题目来演示完整的解题思路:
某计算机主存存取周期为200ns,CPU主频为500MHz。现采用DMA方式从磁盘读取一个4KB的数据块到主存,磁盘的数据传输率为10MB/s。假设采用周期挪用方式,求DMA传送期间CPU被挪用周期占总周期的比例。
解题步骤:
第一步,计算磁盘传送一个字节需要的时间。磁盘传输率10MB/s,即每秒传送10×10^6个字节,每个字节需要的时间是1/(10×10^6) = 100ns。
第二步,计算DMA传送4KB数据需要挪用的存储周期数。每个字节需要挪用一次总线,共4096次,每次占用一个存储周期200ns。所以DMA总共占用总线的时间是4096×200ns = 819200ns。
第三步,计算整个传送过程的总时间。由于磁盘每100ns就准备好一个字节,而DMA每200ns才能挪用一次总线,所以DMA实际上是被主存速度限制的。传送一个字节需要200ns(因为要等一个存储周期),4096个字节总共需要4096×200ns = 819200ns。
第四步,计算CPU被挪用的比例。在819200ns的总时间内,DMA占用了819200ns的总线时间,所以CPU被挪用的比例是100%。这个结果说明,当I/O设备速度很快而主存速度相对较慢时,周期挪用方式下CPU几乎无法访存,这时候可能就不如直接用停止CPU访存方式了。
这道题的关键在于理解:周期挪用方式下,DMA占用总线的时间取决于主存存取周期和数据个数的乘积,而总时间取决于I/O设备传输率和主存存取周期中较慢的那个。很多同学容易把这两个概念搞混。
7. 复习DMA这一块时我踩过的坑和总结的技巧
7.1 别把DMA和中断的优先级搞反
我当年做题的时候,有一道选择题问"DMA请求和中断请求哪个优先级更高",我下意识觉得中断更重要所以优先级更高,结果选错了。正确答案是DMA请求优先级更高。原因前面说过:DMA请求的是总线控制权,如果被中断抢走了总线,DMA传送的数据可能会丢失。而中断请求晚一点响应通常不会造成数据丢失,只是会稍微增加响应延迟。
这个知识点在唐朔飞教材里有明确说明,但很多同学看书的时候一扫而过,做题的时候就容易错。我的建议是:把"DMA请求 > 中断请求 > 程序查询"这个优先级顺序当成常识记下来,做题的时候直接套。
7.2 计算题里"存储周期"和"总线周期"别混用
在做DMA计算题的时候,题目有时候给的是"存储周期",有时候给的是"总线周期",有时候给的是"机器周期"。这三个概念在DMA计算中通常可以近似认为是一样的(都表示一次访存需要的时间),但严格来说有区别。408考试中一般不会在这上面设陷阱,但你要知道题目给的是哪个参数,用对应的数值去算。
另外,如果题目给了CPU主频,你要能算出CPU时钟周期,然后根据题目条件判断一个存储周期等于几个时钟周期。这个换算在综合题里经常用到。
7.3 DMA传送结束后中断CPU,这个中断属于什么类型
这是一个容易被忽略的细节:DMA传送结束后向CPU发出的中断请求,属于可屏蔽中断还是不可屏蔽中断?答案是通常属于可屏蔽中断。因为DMA传送完成并不是什么紧急到不可延迟的事件,CPU可以过一会儿再处理。但有些教材可能不强调这一点,考试中如果考到,你要知道DMA结束中断一般是可屏蔽的。
7.4 王道和唐朔飞的教材怎么配合看
如果你用的是王道复习指导,我建议DMA这一块先看唐朔飞教材的对应章节,把DMA控制器的结构和工作流程搞清楚,然后再做王道的题目。因为王道偏应试,知识点讲得比较浓缩,如果没有教材的基础直接看王道,容易知其然不知其所以然。唐朔飞的教材在DMA这部分讲得比较细,尤其是三种传送方式的对比和时序分析,值得仔细看一遍。
白中英的教材在DMA部分也有独到之处,特别是对DMA控制器内部寄存器的描述比较清晰。如果你时间充裕,两本教材对照着看,效果会更好。
7.5 做真题时注意DMA和其他知识点的综合
DMA很少单独出大题,它经常和总线仲裁、存储器扩展、中断系统等知识点综合出题。比如一道大题可能先问你总线仲裁的方式,再问你DMA传送的时间计算,最后问你中断服务程序的执行流程。这种综合题要求你对整个I/O系统有全局的理解,不能只盯着DMA一个点。
我的建议是:复习到DMA的时候,主动把它和中断、总线、存储器这几个章节串起来想一遍。比如问自己:DMA控制器接管总线的时候,总线仲裁器在干什么?DMA传送的数据经过Cache吗?DMA传送结束后,CPU怎么知道数据已经准备好了?这些问题想清楚了,综合题就不怕了。
8. 从408到实际系统:DMA在真实硬件中的影子
虽然408考试里的DMA是简化过的模型,但它的核心思想在真实硬件中无处不在。你如果玩过STM32,一定听说过串口DMA、SPI DMA、ADC多通道DMA采集这些词。STM32里的DMA控制器和408里讲的DMA控制器在原理上是一模一样的:都是让数据在外设和内存之间直接搬运,不经过CPU。
比如你用STM32做ADC多通道采集,如果不使用DMA,你就得在中断里逐个读取ADC数据寄存器的值,CPU开销很大。使用了DMA之后,你只需要配置好DMA的源地址(ADC数据寄存器)、目的地址(内存数组)、传输长度,启动DMA,然后ADC每转换完一个通道,DMA就自动把数据搬到内存里,全部搬完之后再中断CPU一次。这和408里讲的"DMA传送结束后中断CPU"完全对应。
再比如Linux内核里的DMA子系统,它管理着各种设备的DMA通道,提供了一套统一的API让驱动开发者使用。内核DMA保护机制可以防止恶意设备通过DMA访问不该访问的内存区域,这在408里当然不会讲,但如果你对系统底层感兴趣,这是一个很好的延伸方向。
所以我的体会是:408里的DMA知识不是死记硬背的考点,它是一套真实存在的硬件工作机制的简化模型。你如果在复习的时候能把它和实际硬件对应起来,不仅记得更牢,而且对以后做嵌入式或者系统开发也有实实在在的帮助。
9. 几个容易出选择题的细节补充
最后再补充几个408选择题里经常出现的细节,这些都是我在刷题过程中反复遇到的。
第一个细节:DMA控制器中的字计数器记录的是还需要传送的数据个数,而不是已经传送的个数。每传送一个数据,计数器减一,减到零表示传送结束。有些题目会故意把"减到零"说成"加到某个值",你要注意区分。
第二个细节:DMA方式下,数据传送方向是由控制寄存器中的方向位决定的,而不是由DMA控制器自动判断的。CPU在预处理阶段需要明确告诉DMA控制器是"从设备读到内存"还是"从内存写到设备"。
第三个细节:DMA控制器不是中断控制器,它虽然能发中断,但它的主要功能是控制数据传送,中断只是它通知CPU的一种手段。不要把DMA控制器和中断控制器的功能搞混。
第四个细节:在周期挪用方式下,DMA控制器每传送一个数据都需要发一次总线请求。而在停止CPU访存方式下,DMA控制器只需要在开始传送时发一次总线请求,整批数据传送完毕后释放总线。这个区别在时序图题里经常考。
第五个细节:DMA传送不需要CPU执行指令,所以DMA传送过程中CPU的指令执行不受影响(除了访存被暂停)。但中断方式下,CPU需要执行中断服务程序,指令执行流会被打断。这个区别在比较两种方式效率的时候是核心论据。
把这些细节都吃透,DMA这一块的选择题基本就不会丢分了。大题的话,重点练周期挪用方式的时间计算,把公式和推导过程练熟,考试的时候直接套就行。