思维导图
有需要可以下载:内存管理思维导图PNG
内存的基础知识
- 进程应该放在内存的哪里?
- 操作系统如何记录哪些内存区域已经被分配了,哪些还空闲?
- 当进程运行结束之后,如何将进程占用的内存空间释放?
- 操作系统负责内存空间的分配与回收
- 操作系统需要提供某种技术从逻辑上对内存空间进行扩充
- 操作系统需要实现地址转换功能,负责程序的逻辑地址和物理地址的转换
- 操作系统需要提供内存保护功能,保证各进程在各自存储空间内运行, 互不干扰
什么是内存?
内存是用于存放数据的硬件
程序执行前需要先放到内存中才能被cpu处理
存储单元
如果计算机"按字节编址", 每个存储单元大小为 8 bit
如果计算机"按字编址", 每个存储单元大小为 16 bit
逻辑地址和物理地址
指令的编指一般采用逻辑地址,即相对地址
物理地址 = 起始地址 + 逻辑地址
编译、链接、装入
编译
由编译程序将用户源代码编译成若干个目标模块
链接
由连接程序将编译后形成的一组目标模块以及所需函数连接在一起, 形成一个完整的装入模块
链接的三种方式 :
- 静态链接
- 装入时动态链接
- 运行时动态链接
静态链接
在程序运行之前,先将各目标模块以及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。
装入时动态链接
将各目标模块装入内存时,边装入边链接的链接方式。
运行时动态链接
在程序执行中需要该目标模块时,才对它进行链接。其优点是便于修改和更新,便于实现对目标模块的共享。
装入
由装入程序将装入模块装入内存运行
为了使编程更方便,程序员写程序时应该只需关注指令、数据的逻辑性。而逻辑地址到物理地址的转换(这个过程称为地址重定位),应该由操作系统负责,这样就保证了程序员写程序时不需要关注物理内存的实际情况。
装入的三种方式:
- 绝对装入
- 静态重定位
- 动态重定位
绝对装入
在编译时,如果知道程序将放到内存的哪个位置,编译程序将产生绝对地址的目标代码, 装入程序按照装入模块中的地址,将程序和数据装入内存【灵活性低, 只适合单道程序环境,无操作系统】
静态重定位
又称为可重定位装入。 编译, 链接后的装入模块地址都是从 0 开始的,指令中使用的地址和数据存放的地址都是相对于起始地址而言的逻辑地址。可以根据内存的当前状况将装入模块装入到内存的适当位置。装入时对地址进行"重定位",逻辑地址变换为物理地址(地址变换是在装入时一次完成的)。【早期多道批处理系统】
动态重定位
装入程序把装入模块装入内存后不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。因此装入内存后所有的地址依然是逻辑地址。这种方式需要一个重定位寄存器(存放装入模块的起始位置)的支持。【现代操作系统】
内存保护
上、下限寄存器
在CPU设置一对上、下限寄存器,存放进程的上、下限地址。进程的指令要访问某个地址时,CPU检查是否越界。
重定位寄存器、界地址寄存器
采用重定位寄存器(又称基址寄存器)和界地址寄存器(又称限长寄存器)进行越界检查。重定位寄存器中存放的是进程的起始物理地址。界地址寄存器中存放的是进程的最大逻辑地址。
内存空间的扩充
覆盖与交换
覆盖, 交换, 虚拟存储技术常用于实现内存空间的扩充
覆盖技术
覆盖技术的思想:将程序分为多个段,常用的段常驻内存,不常用的段在需要的时候调入内存
内存中分为一个"固定区" 和若干个"覆盖区",常用的段放在固定区,不常用的段放在覆盖区
缺点:必须由程序员声明覆盖结构, 对用户不透明, 增加了用户的编程负担,覆盖技术只用于早期的操作系统中。
交换技术
交换技术的思想:内存空间紧张时, 系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(即进程在内存与磁盘间动态调度)
1、应该在外存(磁盘)的什么位置保存被换出的进程?
2、什么时候应该交换?
3、应该换出哪些进程?
1.具有对换功能的操作系统中,通常把磁盘空间分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式。总之,对换区的I/o速度比文件区的更快。
2.交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出。
3.可优先换出阻塞进程;可换出优先级低的进程;为了防止优先级低的进程在被调入内存后很快又被换出,有的系统还会考虑进程在内存的驻留时。
虚拟内存
虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。
传统存储管理方式的特征、缺点
1、一次性:作业必须一次性全部装入内存后才能开始运行
- 作业很大时,无法装入导致大作业无法运行
- 大量作业要求运行时内存无法容纳所有作业,导致多道程序并发度下降
2、驻留性:一旦作业被装入内存,就会一直驻留在内存中,直到作业运行结束,这样会导致内存中驻留大量的,暂时用不到的数据,浪费内存资源。
高速缓冲技术的思想
将近期会频繁访问到的数据放到更高速的存储器中,暂时用不到的数据放在更低速存储器中。
虚拟内存的定义和特征
-
在程序装入时,将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。
-
在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息由外存调入内存,然后继续执行程序。请求调页/段
-
内存空间不够时, 操作系统负责将内存中暂时用不到的信息换出到外存。页面置换
-
在用户看来,就有一个比实际内存大很多的内存,这就叫虚拟内存。
虚拟内存的最大容量是由计算机的地址结构(CPU的寻址范围)确定的
虚拟内存的实际容量 = min(内存容量+外存容量,CPU寻址范围)
特征:
多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。
对换性:在作业运行时无需一直常驻内存,而是允许在作业运行过程中,将作业换入、换出。
虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量远大于实际容量。
虚拟内存的实现
请求分页存储管理
页表机制
缺页中断机构
在请求分页操作系统中,每当要访问的页面不在内存时,便产生一个缺页中断, 然后由操作系统的缺页中断处理程序处理中断。
此时缺页的进程阻塞,放入阻塞队列,调页完成后再将其唤醒, 放回就绪队列。
- 如果内存中有空闲块,则为进程分配一个空闲块,将所缺页面装入该块,并修改页表中相应的页表项
- 如果内存中没有空闲块,则由页面置换算法选择一个页面淘汰,若该页面在内存期间被修改过,则要将其写回外存,未修改过的页面不用写回外存。
缺页中断是因为当前执行的指令想要访问目标页面未调入内存而产生的,因此属于内中断(故障) 。
页面置换算法
最佳置换算法 OPT
优先淘汰以后永不使用或者在最长时间内不会使用的页面,保证最低的缺页率。
但是操作系统无法预判页面访问序列,这种算法是无法实现的。
先进先出置换算法 FIFO
优先淘汰最早进入内存的页面。
实现 :将调入内存的页面根据调入的先后顺序排成一个队列,需要置换页面的时候选择队首的页面。
实现简单;算法性能差,不适应进程实际运行时的规律,可能出现 Belady 异常。
最近最久未使用算法 LRU
优先淘汰最近最久未使用的页面。
性能好,但实现起来需要专门的硬件支持,算法开销大。
时钟置换算法 CLOCK / 最近未用算法 NRU
同一种算法。
循环扫描各页面,第一轮淘汰访问位 = 0 的,并将扫描过的页面访问位改为1。若第一轮没选中,则进行第二轮扫描。
实现简单,算法开销小;但未考虑页面是否被修改过。
简单的时钟置换算法仅考虑到了一个页面最近是否被访问过,但是事实上,如果被淘汰的页面没有被修改过,就不需要执行I/O操作写回外存。只有被淘汰的页面被修改过时,才需要写回外存。
因此, 除了考虑一个页面最近有没有被访问过之外,操作系统还应该考虑页面有没有被修改过。
在其他条件都相同时,应该优先淘汰没有修改过的页面, 避免I/O操作,这就是改进型的时钟置换算法的思想。
改进型的时钟置换算法
利用(访问位,修改位) 的形式表示各页面状态
步骤 | 对应页面 |
---|---|
第一轮 : 找第一个 (0, 0)的帧用于替换 ( 不修改标志位 ) | 最近没访问且没修改 |
第二轮 : 找第一个 (0, 1)的帧用于替换 ( 将所有扫描过的帧访问位设为0) | 最近没访问但修改过 |
第三轮 : 找第一个 (0, 0)的帧用于替换 ( 不修改标志位 ) | 最近访问过但没修改 |
第四轮 : 找第一个 (0, 1)的帧用于替换 | 最近访问过也修改过 |
算法开销小,性能也不错。
Belady异常 —— 当为进程分配的物理块数增大时,缺页次数不减反增的异常现象。
页面分配、置换策略
驻留集
请求分页存储管理器中给进程分配的物理块的集合。
【系统给进程分配了n各物理块的另一种表述:驻留集大小为n】
在采用虚拟存储技术的系统中, 驻留集的大小一般小于进程的总大小
- 如果驻留集太小,会导致缺页频繁,系统要花大量的时间来处理缺页,实际用于进程推进的时间很少。
- 如果驻留集太大,会导致多道程序并发度下降,资源利用率降低。
策略
固定分配:操作系统为每个进程分配一组固定数目的物理块,在进程运行期间不再改变。即,驻留集大小不变。
可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可根据情况做适当的增加或减少。即,驻留集大小可变。
局部置换:发生缺页时只能选进程自己的物理块进行置换。
全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程。
可变分配全局置换:只要缺页就给分配新物理块。
可变分配局部置换:要根据发生缺页的频率来动态地增加或减少进程的物理块。
何时调入页面
何处调入页面
抖动现象
刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称
为抖动,或颠簸。
产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数( 分配给进程的物理块不够)。
工作集
工作集:指在某段时间间隔里,进程实际访问页面的集合。
驻留集:指请求分页存储管理中给进程分配的内存块的集合。
- 一般来说,驻留集大小不能小于工作集大小,否则进程运行过程中将频繁缺页。
请求分段存储管理
请求段页式存储管理
内存空间的分配和回收
连续分配管理方式
为用户进程分配的必须是一个连续的内存空间。
单一连续分配
在单一连续分配的方式中, 内存被分为系统区和用户区, 系统区用于存放操作系统的相关数据,用户区用于存放用户进程的相关数据。内存中只能有一道用户程序,用户程序独占整个用户区空间。
- 优点 : 实现简单, 无外部碎片; 可以采用覆盖技术扩充内存; 不一定需要采取内存保护
- 缺点 : 只能用于单用户, 单任务的操作系统中; 有内部碎片; 存储器利用率极低
内部碎片 : 分配给某进程的内存区域有一部分没有用上,即存在" 内部碎片 ".
外部碎片 : 内存中的某些空闲分区由于太小而难以利用
固定分区分配
在产生了支持多道程序的系统后,为了能在内存中装入多道程序而互相之间不产生干扰,将整个用户区划分为若干个固定大小的分区(分区大小可以相等也可以不相等),在每个分区中只能装入一道作业, 形成了最早的可运行多道程序的内存管理方式。
操作系统建立分区说明表来实现各个分区的分配和回收。
每个表对应一个分区,通常按分区大小排列。每个表项包括对应分区的大小,起始地址,状态。
用数据结构的数组(或链表)即可表示这个表。
当某用户程序要装入内存时,由操作系统内核程序根据用户程序大小检索该表,从中找到一个能满足大小的、未分配的分区,将之分配给该程序,然后修改状态为 ” 已分配 “ 。
- 优点:实现简单,无外部碎片。
- 缺点:1、当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这又会降低性能。2、会产生内部碎片,内存利用率低。
动态分区分配
动态分区分配又称为可变分区分配, 这种分配方式不会预先划分内存分区。而是在进程装入内存时根据进程大小动态地建立分区,并使得分区的大小正好适合进程的需要。
系统要用什么样的数据结构记录内存的使用情况?
两种常用的数据结构:空闲分区表、空闲分区链
空闲分区表:每个空闲分区对应一个表项。表项中包含分区号、分区大小、分区起始地址等信息。
空闲分区链:每个分区的其实部分和末尾部分分别设置前向指针和后向指针。起始部分处还可记录分区大小等信息。
动态分配不会产生内部碎片, 而会产生外部碎片, 外部碎片可以通过" 紧凑"的方式解决。
动态分区分配算法
1、首次适应算法
每次都从低地址开始查找,找到第一个能满足大小的空闲分区。
实现:把空闲分区按地址递增的次序排列。
每次分配内存时顺序地查找空闲分区链,找到大小能满足要求的第一个空闲分区。
优点:综合看性能最好。算法开销小,回收分区后一般不需要对空闲分区队列重新排序。
2、最佳适应算法
优先使用小的空闲分区
实现: 空闲分区按容量递增次序链接。
每次分配内存时顺序查找空闲分区链, 找到大小能满足要求的第一个空闲分区。
优点:会有更多的大分区被保留下来,更能满足大进程需求。
缺点:每次都选择最小的分区进行分配,会留下越来越多的容量很小难以利用的内存块,即产生很多的外部碎片;算法开销大,回收分区后可能需要对空闲分区队列重新排序。
3、最坏适应算法
优先使用大的空闲分区
实现: 空闲分区按容量递减次序链接
优点:可以减少难以利用的小碎片。
缺点:每次都选用最大的分区进行分配,当较大的连续空闲区被小号之后,如果有大进程到来则没有内存分区可以利用;算法开销大。
4、邻近适应算法
在首次适应算法的基础上,每次都从上次查找结束的位置开始查找空闲分区链(表),找到大小能满足的第一个空闲分区。
优点:不用每次都从低地址的小分区开始检索。算法开销小。
缺点:邻近适应算法导致无论低地址还是高地址的空闲分区都有相同的概率被使用,也就导致了高地址部分的大分区更可能被使用划分为小分区,最后导致没有大分区可用。
算法开销大:最佳适应法, 最坏适应法 ( 需要经常排序)
算法开销小:首次适应算法, 邻近适应算法
非连续分配管理方式
为用户进程分配的可以是一些分散的内存空间。
基本分页存储管理
思想:把内存分为一个个相等的小分区, 再按照分区大小把进程拆分成一个个小部分.
页框和页面
页框:将内存大小分为一个个大小相等的分区,每个分区就是一个 “ 页框 ”,或称 “ 页帧 ”、“ 内存块 ”、“ 物理块 ”。每个页框有一个编号,即 “ 页框号 ”,或称 “ 内存块号 ”、“ 页帧号 ”、“ 物理块号 ” ,页框号从 0 开始。
页面:将用户进程的地址空间也分为与页框大小相等的一个个区域,称为 “ 页 ” 或 “ 页面 ”。每个页面也有一个编号,即 “ 页号 ”,页号也是从 0 开始。
imp:
- 进程的最后一个页面可能没有一个页框那么大。因此。页框不能太大,否则可能产生过大的内部碎片。
- 操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说,进程的页面与内存的页框有一一对应的关系。
- 各个页面不必连续存放,也不必按先后顺序来,可以放到不相邻的各个页框中。
地址转换的实现
1、确定逻辑地址的 " 页号 " P ----- 页号 = 逻辑地址 / 页面长度
2、找到P号页面在内存中的起始地址 ( 需要查找页表 ) ----- 操作系统需要用某种数据结构记录进程各个页面的起始位置
3、确定逻辑地址的 " 页内偏移 " W ----- 页内偏移量 = 逻辑地址 % 页面长度
4、物理地址 = P号页面在内存中的起始地址 + 页内偏移量W
为了方便计算页号和页内偏移量,页面大小一般设置为2的整数幂
结论:如果每个页面大小为 2^k B,用二进制数表示逻辑地址,则末尾 K 位即为页内偏移量,其余部分就是页号。
页表
为了能知道进程的每个页面在内存中存放的位置,操作系统要为每个进程建立一张页表。
- 一个进程对应一张页表
- 进程的每一页对应一个页表项
- 每个页表项由 “ 页号 ” 和 “ 块号 ” 组成
- 页表记录进程页面和实际存放的内存块之间的对应关系
imp:页表中的页号是 “ 隐含 ” 的,可以不占用存储空间。
假设某系统物理内存大小为 4GB, 页面大小为 4KB, 则每个页表项至少应该为多少字节?
基本地址变换机构
用于实现逻辑地址到物理地址转换(借助进程的页表)的一组硬件机构
-
通常会在系统中设置一个页表寄存器(PTR),存放页表在内存中的起始地址 F 和页表长度 M。
-
进程未执行时,页表的始址和页表长度放在进程控制块(PCB)中;
-
当进程被调度时,操作系统内核会把它们放到页表寄存器中。
设页面大小为L,逻辑地址A到物理地址E的变换过程如下:
① 计算页号 P 和页内偏移量 W。
② 比较页号 P 和页表长度M,若P≥M,则产生越界中断,否则继续执行。(注意:页号是从0开始的,而页表长度至少是1,因此P=M时也会越界)
③ 页表中页号 P 对应的页表项地址 = 页表起始地址 F + 页号 P * 页表项长度,取出该页表项内容b,即为内存块号。
④ 计算E= b * L + W,用得到的物理地址 E 去访存。
imp:页表长度指的是这个页表中总共有几个页表项,即总共有几个页;页表项长度指的是每个页表项占多大的存储空间;页面大小指的是一个页面占多大的存储空间
- 一共需要访问两次内存:第一次用来查页表,第二次用于访问目标内存单元。
- 页式管理中地址是一维的。
- 为了方便找到页表项,页表一般是放在连续的内存块的。
具有快表的地址变换机构
局部性原理
-
时间局部性
如果执行了程序中的某条指令, 那么不久之后这条指令很有可能再次执行; 如果某个数据被访问过, 不久之后该数据很可能再次被访问。
( 程序中存在大量的循环 )
-
空间局部性
一旦程序访问了某个存储单元, 在不久之后, 其附近的存储单元也很有可能被访问到。
( 很多数据在内存中连续存放 )
快表(TLB)
快表又成为联想寄存器(TLB),是一种访问速度比内存块很多的高速缓冲存储器,用来存放当前访问的若干页表项, 以加速地址变换的过程. 与此对应的,内存中的页表常称为慢表。
地址变换的过程
① CPU给出逻辑地址,由某个硬件算得页号、页内偏移量,将页号与快表中的所有页号进行比较。
② 如果找到匹配的页号,说明要访问的页表项在快表中有副本,则直接从中取出该页对应的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表命中,则访问某个逻辑地址仅需一次访存即可。
③ 如果没有找到匹配的页号,则需要访问内存中的页表,找到对应页表项,得到页面存放的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表未命中,则访问某个逻辑地址需要两次访存(注意:在找到页表项后,应同时将其存入快表,以便后面可能的再次访问。但若快表已满,则必须按照一定的算法对旧的页表项进行替换)。
两级页表
单级页表存在的问题
1、假设某计算机系统按字节寻址,支持32位逻辑地址,采用分页存储管理,页面大小为4KB,页表项长度为4B。
4KB = 2^12B,因此页内地址要用12位表示,剩余20位表示页号。
因此,该系统中用户进程最多有2^20页。相应的,一个进程的页表中,最多会有220个页表项,所以一个页表最大需要220 * 4B = 222B。一个页框(内存块)大小为 4B,所以需要2^22 / 2^12 = 2^10个页框存储该页表。而页表的存储是需要连续存储的,因为根据页号查询页表的方法:K号页对应的页表项的位置 = 页表起始地址 + K * 4B,所以这就要求页表的存储必须是连续的。当页表很大时,需要占用很多个连续的页框。
回想一下,当初为什么使用页表,就是要将进程划分为一个个页面可以不用连续的存放在内存中,但是此时页表就需要1024个连续的页框,与当时的目标相悖。
2、根据局部性原理可知,很多时候,进程在一段时间内只需要访问某几个页面就可以正常运行了。因此没有必要让整个页表都常驻内存。
解决方法
可将长长的页表进行分组,使每个内存块刚好可以放入一个分组,再将各组离散地放到各个内存块中。
另外,为离散分配的页表再建立一张页表,称为页目录表,或称外层页表,或称顶层页表
实现地址转换
1、按照地址结构将逻辑地址拆成三个部分。
2、从PCB中读取页目录起始地址,再根据一级页号查页目录表,找到下一级页表在内存中存放位置。
3、根据二级页号查表,找到最终想要访问的内存块号。
4、结合页内偏移量得到物理地址。
细节
- 若采用多级页表机制,则各级页表的大小不能超过一个页面。
- 两级页表的访存次数分析(假设没有快表机构)
- 第一次访存:访问内存中的页目录表
- 第二次访存:访问内存中的二级页表
- 第三次访存:访问目标内存单元
基本分段存储管理
分段:进程的地址空间会按照自身的逻辑关系划分为若干个段, 每个段都有一个段名, 每段从0开始编址。
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
分段系统的逻辑地址结构
段号的位数决定了每个进程最多可以分为几个段
段内地址位数决定了每个段的最大长度是多少
段表
查找过程
需要两次访存:第一次访存查内存中的段表,第二次访存访问目标内存单元。
分段、分页管理的对比
- 对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段。
- 地址空间的维度:分页是一维地址空间,分段是二维的。
- 大小是否可以改变:页的大小不可变,段的大小可以动态改变。
- 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。
页是信息的物理单位。
段是信息的逻辑单位。
段页式内存管理方式
程序的地址空间划分成多个拥有独立地址空间的段,每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。
分页, 分段管理的优缺点分析
段页式系统的逻辑地址结构
由段号、页号位数、页内偏移量组成
段号的位数决定了每个进程最多可以分几个段
页号位数决定了每个段最大有多少页
页内偏移量决定了页面大小、内存块大小是多少
" 分段 " 对用户是可见的,而将各段"分页"对用户是不可见的,系统会根据段内地址自动划分页号和段内偏移量,因此段页式管理的地址结构是 " 二维 " 的。
段表、页表
每一个进程对应一个段表,每一个段又对应一个页表,因此一个进程可能对应多个页表。
每个段对应一个段表项,每个段表项由段号、页表长度、页表存放块号(页表起始地址)组成。
每个段表项长度相等,段号是隐含的。
每个页面对应一个页表项,每个页表项由页号、页面存放的内存块号组成。
每个页表项长度相等,页号是隐含的。
查找过程
1、由逻辑地址得到段号、页号、页内偏移
2、段号与段表寄存器的段长度比较,检查是否越界
3、由段表始址, 段号找到对应段表项
4、根据段表中记录的页表长度,检查页号是否越界
5、由段表中的页表地址, 页号得到查询页表,找到相应页表项
6、由页面存放的内存块号,页内偏移得到最终的物理地址
7、访问目标单元
需要三次访存,第一次查段表,第二次查页表,第三次访问目标单元。