北京外国语大学《编译原理》课件-第5章 运行环境

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
5.2 运行时数据空间的组织
5.2.1 运 行 时 内 存 的 划 分 与 数 据 空 间 的 存 储 分 配 策 略
程序运行时,内存用来保存可执行代码和代码所操作的数据。 可执行代码的大小在编译时可以静态确定,因此可以把它放在静 态数据区。而对于数据,可以有三种存储方式,对应三种组织形 式的数据区,它们分别是静态数据区、栈和堆。静态数据区用于 存放一对一的绑定且编译时就可确定存储空间大小的数据;栈用 于存放一对多的绑定且与活动同生存期的数据;堆用于存放与活 动生存期不一致且可以动态生成和撤消的数据,典型的如PASCAL 中new(p)和dispose(p)等。栈与堆的典型安排如图5.4所示,它们的 增长空间是共享的。
第5章 运行环境 顺序执行的程序的最大特征是程序的执行在时间上是顺序的 和排他的。即一个程序的运行轨迹由若干个顺序或嵌套的活动组 成,并且在程序执行的任一瞬间,有且仅有一个活动正在活动。 假想时间是一支笔,则任何一个顺序程序的执行过程(控制流)是 一个“一笔画”,于是顺序程序运行时的控制流满足以下两点:
第5章 运行环境 现在有两个关于名字的问题:名字的声明与名字的绑定。它们 都需要有对应的存储空间,而存储空间的对应方式,一个是静态的, 一个是动态的。声明时我们关心的是声明的作用域,即当一个名字 被引用时,在不同的作用域中与该名字的不同声明结合;绑定时我 们关心的是绑定的生存期,即当一个名字在运行时被实际分配的存 储单元,名字与存储单元结合的这段时间被称为绑定的生存期,显 然这个生存期应该和名字的生存期是一致的。
第5章 运行环境
过程的嵌套实际上是过程定义的嵌套,是指在一个过程的 定义中包含另外一个过程的定义,这是一个静态的概念,仅从 读源程序就可以确定过程之间的嵌套定义关系。而活动的嵌套 实际上是活动调用的嵌套或者活动生存期的嵌套,是指当一个 活动正在执行时,又调用了另外的活动,这是一个动态的概念, 它们的嵌套关系是由确定活动执行轨迹的条件(例如参数等)动 态确定的。
第5章 运行环境
静态数据区中变量的地址可以有两种表示方法。以图5.5中变
量X的地址Δx为例,它可以相,由于数据的静态特性,所有的base i相对于base的偏移量 均是编译时可以确定的常量,因此两种表示方法没有实质性区别。 但是,相对于base i寻址的方法,可以将静态存储分配与栈式存储 分配的方法统一起来,同时也有利于将各活动记录中的共享数据 提取出来进行统一处理。
采用静态分配策略的存储空间可以组织如下,编译器为每 个模块分配一块连续的存储空间,根据模块中名字的类型确定 它所需空间的大小。为了称呼上的统一,我们称每个模块的数 据区为活动记录。由于每个活动记录的大小均是确定的,所以 若干活动记录组成的连续存储空间的大小也是确定的。这一确 定的空间在程序运行时一并装入内存,而不管各活动记录是否 在某次特定的运行中被使用,程序运行时不再有对存储空间的 分配。
表5.1 静 态 与 动 态
第5章 运行环境 在名字绑定的概念下,对一个变量的赋值,实际上是通过两 步映射来实现的。源程序中的一个名字,需要经过名字的绑定将 名字映射到一个实际的存储空间, 然后经过赋值将此存储空间映射 到一个实际的值。名字到存储空间的映射被称为“环境”,存 储 空间到值的映射被称为“状态”。由于存储空间对应的是左值,而 值对应的是右值,因此我们也可以说,环境将名字映射到左值,状 态将左值映射到右值,或者说环境改变存储空间,状态改变值。
第5章 运行环境 图5.2 活动树与控制栈
第5章 运行环境 5.1.2 控制栈与活动记录
一个完整程序执行的控制流,恰好是对它的活动树的一次深 度优先遍历。根据顺序执行程序的控制流特性,活动树上各节点 之间具有下述关系:
① 同一层次的活动生存期不交; ② 任一时刻,处在生存期的活动构成一条从根到某节点的路 径;
第5章 运行环境
北京外国语大学《编译原理》 第5章 运行环境
1. 过程的动态特性 2. 运行时数据空间的组织 3. 栈式动态分配 4. 本章小结
第5章 运行环境
5.1 过程的动态特性
5.1.1 过程与活动 过程的每一次运行(或执行)被称为一次活动(activation)。活
动是一个动态的概念,除了设计为永不停机的过程(如操作系统 等),或者是因设计错误而出现死循环的过程之外,任何过程的 活动均有有限的生存期(life time)。
第5章 运行环境
活动树的实质是它反映了顺序执行程序的调用和时序关系, 它把每个活动的生存期缩小到了一点。也就是说,如果我们关 心的仅是活动之间的控制流和它们的生存期,而不关心活动究 竟执行了多少时间的话,活动树是最好的表示形式。
第5章 运行环境 例5.1 阶乘函数的计算可以用下述程序test实现,首先调用 get_line(n)得到一个整型数值,然后调用递归函数f(n)计算出n的 阶乘,最后将结果输出。令n=4,则程序运行时活动的轨迹如图 5.1(a)所示。其中纵向是时间轴,横向反映控制流,即调用与返 回关系。如果忽略活动执行的时间,仅考虑控制流的流向,即 将图5.1(a)各活动执行时间均压缩为一个点,且将其旋转90o0, 则演变成为如图5.1(b)所示的活动树。树的边是双向的,既表示 调用又表示返回。
第5章 运行环境 图5.4 内存空间划分
第5章 运行环境 三种数据区对应着下述三种不同的分配策略,编译器具体 实现时,根据语言机制的特性,可以采用三种方式的其中若干 种。
① 静态分配策略:编译时安排所有数据对象的存储; ② 栈分配策略:按栈的方式管理运行时的存储; ③ 堆分配策略:在运行时根据要求从堆数据区动态地分配和 释放存储。
③ 路径上各节点生存期是嵌套的(后进先出)。
第5章 运行环境 换句话说,任何时刻仅需要为所有处在生存期的活动提供它 们的活动场所,称为运行环境。根据上述的②和③,很显然,运行 环境的最佳数据结构应该是一个栈,称为控制栈。而栈上的每个 节点是每个活动的运行环境,称为活动记录(activation record)。 每个活动开始时,就为它分配一个活动记录,即将此活动记录分 配在栈顶上。活动的整个生命期中活动记录一直存在,而当活动 结束时,将它从当前栈顶取消。控制栈的栈顶一般由top指示, 为了提高效率,top一般放在寄存器中。
第5章 运行环境
5.1.3 名字的绑定 定义5.3 运行时为名字X分配存储空间S,这一过程称为绑
定 (binding)。
绑定是名字X与存储空间S的结合。此处,名字X是一个对象, 它既可以是数据对象,如变量,与之结合的是一个存储单元,也 可以是操作对象,如过程,与之结合的是一段可执行的代码。本 章的讨论仅限于X是一个数据对象。
同样,在名字绑定的概念下,对一个常量的赋值实质上就是 直接将名字与一个具体的值绑定,或者说环境将名字映射到右值, 或者说环境直接改变值。变量与常量的映射关系分别如图5.3(a)、 (b)所示。由于表示常量的名字没有左值,因此,常量是不能通过 赋值句被改变的。
第5章 运行环境
例5.4 对于赋值句x := 3.14,首先为x分配一个存储单元S, 然后将3.14赋值给S。对于常量声明const pi=3.14,直接将pi与 3.14绑定,于是在程序运行的任何时刻,pi的值不能被改变。它 们的映射关系分别如图5.3(c)、(d)所示。
第5章 运行环境
例5.2 考虑例4.14的快排序过程。如果忽略partition中对 exchange的调用,则对于某个初始数据,sort的活动树可能如图 5.2(a)所示。其中的s、r、q、p分别是sort、readarray、quicksor partition的缩写。图5.2(a)反映了活动的嵌套,而图4.11(a)反映了 程的嵌套。
第5章 运行环境 图5.5 静态存储分配
第5章 运行环境 静态分配的特点,使得它所适用的程序设计语言在某些方 面受到限制: ① 数据对象的大小和它在内存中位置的限制必须在编译时确 定,如数组的大小不能是动态的; ② 不允许程序递归,因为一个过程的所有活动使用同样的 名字绑定,即绑定是一对一的;
③ 不允许动态生成数据,因为没有运行时的存储分配机制。
第5章 运行环境
定义5.1 活动的生存期是指从进入活动的第一条指令执行 到离开此活动前的最后一条指令执行的这段时间,其中包括调用 其它过程时其它活动的生存期。
活动之间存在两种调用关系:如果活动A调用活动B,从B中 退出后又调用活动C,B和C是被顺序调用的活动,显然被顺序调 用的活动的生存期是不交的;如果活动A调用活动B,而活动B又 调用活动C,B和C是被嵌套调用的关系。特别需要指出的是,活 动的嵌套与过程的嵌套是两个截然不同的概念。
第5章 运行环境
编译器怎样对存储空间进行组织和采用什么样的存储分配 策略,很大程度上取决于程序设计语言中所采用的机制,如过 程能否递归、过程能否嵌套、过程调用时参数如何传递、哪些 实体可以作为参数和返回值、是否允许动态的为对象分配和撤 销存储空间、存储空间是否必须显式地释放,等等。
第5章 运行环境
① 控制流是连续的; ② 过程间的控制流可以用树来表示。
第5章 运行环境 定义5.2 用来描绘控制进入和离开活动方式的树结构被称 为活动树,在活动树中:
① 每个结点代表过程的一个活动; ② 根代表主程序的活动; ③ 结点a是结点b的父亲,当且仅当控制流从a的活动进入b的活 动;
④ 结点a处于结点b的左边,当且仅当a的生存期先于b的生存期
第5章 运行环境
图5.1 顺序执行的程序的控制流 (a) 活动的调用关系与生存期;(b) 仅反映控制流的活动树
第5章 运行环境 procedure test is n : integer; procedure f(n:integer) return integer is begin if i<2 then return 1; else return n*f(n-1); end i end f; begin get_line(n); n:=f(n); put_line(n); end test;
第5章 运行环境
活动记录中至少应该存放两类信息: ① 控制信息:用于控制活动的正确调用与返回和用于控制 活动记录的正确切换; ② 访问信息:用于为当前活动提供对数据,如本地数据和 非本地数据的访问。
第5章 运行环境
例5.3 图5.2(b)给出了快排序程序运行时的某个状态,从s开 始,实线条所链接的活动构成了活动树上的一条路径,也是控 制栈的一个状态,在当前状态下,栈中共有4个节点,分别是s、 q(1,9)、q(1,3)、q(1,0)。这些活动均处在它们的生存期,但是只 有栈顶的q(1,0)是正在活动的活动。
第5章 运行环境
图5.3 变量与常量名字的映射 (a) 变量名字的映射;(b) 常量名字的映射;(c) x:=3.14的映射;(d) pi=3.14的映射
第5章 运行环境
在允许过程递归的情况下,当一个活动还没有执行完成时, 可能又会进入同一过程的另一个活动。为了同时保存两个同一过 程活动的运行环境,同一作用域中的一个名字在运行时可能会被 分配多个存储空间,也就是说,同一作用域中的一个名字可以同 时绑定到多个存储单元。例如图5.2(b)中同一个快排序过程的三 个活动的活动记录q(1,9)、q(1,3)和q(1,0)均被放在控制栈中,相 同参数在三个活动记录中有不同的存储空间用于存放不同的值, 从而使得在快排序的递归调用中,能够通过实在参数的值进行正 确的排序操作。因此,环境是一个一对多的映射。同样,由于一 个存储单元可以存放不同的值,状态也是一个一对多的映射。
第5章 运行环境
5.2.2 静态与动态分配简介 1.静态分配策略 在静态分配中,名字在程序编译时与存储空间结合,运行时
不再改变,每次过程活动时,过程中的名字映射到同一存储单元。 这种性质允许局部名字的值在活动停止后仍能保持,即当控制再 次进入活动时,变量的值和控制上一次离开时相同。
第5章 运行环境
相关文档
最新文档