720 likes | 929 Views
第十三章 运行时存储空间的组织. 第一节 变量及存储分配 程序投入运行的必要条件: 一组可运行的代码 一个运行环境:分配空间、提供运行信息. 一 . 程序的存储空间 1. 代码空间 : 线性存放着目标指令序列 在 GAM 中 , 当前执行的指令位置由指令指针 ip 指示。 2. 数据空间 (1) 内容 : 变量、常数、控制和管理信息、描述符等. (2) 静态分配 : 在运行前就可确定数据空间的大小 , 在编译时刻就能进行的存储分配 (3) 动态分配 : 运行时才能进行的存储分配
E N D
第十三章 运行时存储空间的组织 第一节 变量及存储分配 程序投入运行的必要条件: • 一组可运行的代码 • 一个运行环境:分配空间、提供运行信息
一. 程序的存储空间 1. 代码空间: 线性存放着目标指令序列 在GAM中, 当前执行的指令位置由指令指针ip指示。 2. 数据空间 (1)内容: 变量、常数、控制和管理信息、描述符等
(2)静态分配: 在运行前就可确定数据空间的大小, 在编译时刻就能进行的存储分配 (3)动态分配: 运行时才能进行的存储分配 • 栈分配: 因变量生存期的嵌套性 • 堆分配: 因生存期的随机交叉特性
二. 活动记录 一个程序单元的一次激活所需的信息管理是通过相应的活动记录来实施的。一个单元的每次激活,都应建立相应的活动记录,它是单元实例的一部分。
返回地址 动态链 1. 活动记录的内容 (1)返回地址 (2)动态链和静态链 (3)形式单元 (4)局部变量或其描述符,以及临时变量 静态链 形式单元 变量存储区
2. 活动记录的特点 除了变量存储区外, 其余部分存储长度编译时可以确定, 则元素i的地址可用下式计算 D+offset(i) 其中, offset(i)是i在活动记录中的位移; D是活动记录的首地址
三. 变量的存储分配 1. 静态变量: 不管在程序单元的哪一次活动中, 均绑定于相同的存储位置 条件是: 活动记录, 变量的存储位置在编译时可以确定 2. 半静态变量: 编译时确定相对位置, 单元被激活后, x绑定于D+offset(x) 条件是: 语言允许递归调用
3. 半动态变量: 动态数组; 编译时在活动记录中建立描述符 例: [1..m] int a; [1..n] int b; 4. 动态变量: 动态分配。编译时在活动记录中为动态变量设置二个指针, 一个指向该变量的描述符, 另一个指向该变量的存储空间
四. 存储分配模式 1. 静态分配 只允许静态变量, 变量与存储区域的绑定关系在编译时便可建立, 并完成存储分配。 不允许递归调用, 不允许动态数组, 不允许动态类型的数据对象, 即不允许有非静态变量。如: FORTRAN语言。
2. 栈式分配 • 各单元之间的调用关系遵循“后进先出”模式; • 活动记录的建立和撤消也满足“后进先出”模式; • 用栈分配活动记录; • 分配方法: 当激活一个程序单元时, 其活动记录就动态地分配于栈顶。
如:P调用Q,Q调用R ... R的活动记录 ... Q的活动记录 ... P的活动记录
3. 堆分配 由于动态变量表示的数据对象, 它的长度、个数都有可能在执行中改变, 即在其生存期中动态改变, 就不可能在栈上为这样的对象作分配。 出现下列情况时, 必须用堆式分配: (1)单元活动结束后, 局部变量的值还需保留; (2)调用单元与被调用单元的生存期不满足嵌套关系, 即出现交叉现象。
4. 存储空间的组织 代码 静态数据 栈 堆
第二节 静态分配 O. FORTRAN语言的特点 1. 模块结构 一个主程序段和若干个(可以是0个)辅程序段组成; 辅程序段可以是子程序、函数段或数据块;各段可以独立编译。 2. 说明语句无严格语序, 且具有显式说明和隐式说明。
例: INTEGER A, B DIMENSION X, Y DIMENSION B(10) REAL X(20) INTEGER Y(10,20) COMMOM A, X EQUIVALENCE (I, B(4)), (X(2), Z)
3. 公用(COMMON)和等价(EQUIVALENCE)语句建立了数据空间的同一性。 4. 不允许过程的递归性; 每个数据名所需的存储空间大小都是常量; 所有数据名的性质是确定的。
一. FORTRAN程序运行时的结构 由FORTRAN的特点知,每个单元的活动记录及变量在运行前便可建立,其生存期延续到整个程序结束。
1. FORTRAN程序编译过程 (1)编译时,变量约束于活动记录的常量位移,对变量的引用可用二元式(单元名,位移)表示;转换控制语句中对指令的引用也可翻译成二元式(单元名,指令在代码段中的位移)。
(2)连接时,须将程序所有单元连接起来,将代码段分配到代码区,活动记录分配到数据区。同时将编译时的二元式转换为地址。(2)连接时,须将程序所有单元连接起来,将代码段分配到代码区,活动记录分配到数据区。同时将编译时的二元式转换为地址。 (3)装入时,将连接后的可重定位代码程序装入主存储器并定位,成为可直接执行的机器代码,并将ip指向程序第一条指令,使程序处于可执行状态。
2. FORTRAN程序在GAM中的存储结构 全局数据活动记录 ip 单元1代码段 单元1活动记录 单元2代码段 单元2活动记录 …... …... 单元n代码段 单元n活动记录
二. 运行环境的转换 约定: • d [i, j] —— 单元i的活动记录中位移为j的存储单元内容 • c [i, j] —— 单元i的代码段中位移为j的指令 • D [ m ],C[m]——连接后的实际存储地址 • & X —— X 的地址
1. GOTO X语句 ip := & c [ i , j ] 其中, c[i, j]是编译后标号X对应的二元式
2. 过程调用语句CALL P (1)保存返回地址 d [P , 0 ] := ip + 2 (2)转入P ip := & c [ P , 0 ] 连接后: D [m ] := ip + 2 ip := n 其中, m为P的活动记录的首址;n为P代码段首址
3. 返回语句 RETURN 取返回地址 ip := d [ P , 0 ] 连接后 ip := D [m ] 其中 m 是单元P的活动记录的首址
举例: INTEGER I , J /*主程序*/ COMMON I CALL X GOTO 10 10 CONTINUE END SUBROUTINE X /*子程序*/ INTEGER K , J COMMON I K = 5 I = 6 J = I + K RETURN END
0 D[3]:=ip+2 1 ip:=5 2 ip:=3 3 noop 4 halt 5 D[4]:=5 6 D[0]:=6 7 D[5]:=D[0]+D[4] 8 ip:=D[3] 9 halt 代码存储器
ip=0 I common 0 1 返回地址 main 2 J 返回地址 3 4 K X J 5 数据存储器
ip=5 I common 0 1 返回地址 main 2 J 返回地址 3 2 4 K X J 5 数据存储器
ip=7 6 I common 0 1 返回地址 main 2 J 返回地址 3 2 4 K 5 X J 5 数据存储器
ip=2 I common 0 6 1 返回地址 main 2 J 返回地址 3 2 4 5 K X J 5 11 数据存储器
第三节 栈式分配 • 语言特点:允许递归,允许动态数组,允许过程嵌套定义
一. 只含半静态变量的栈式分配 1. 特点: 仅允许递归调用 变量及活动记录长度可静态确定 一个单元可多次激活而有多个实例,每个活动记录是在单元每次激活时动态建立并与代码段建立绑定关系的
2. 处理方法 (1)设置当前栈指针current,表示当前活动记录的开始位置 (2)指针free表示数据存储器下一个可用单元 (3)变量绑定于它在活动记录中的常数位移i,变量通过current变址访问 (4)A调用B时,在A活动记录的栈顶之上建立起绑定于B的当前实例的活动记录 (5)从B返回时,释放其活动记录
3. 动态连接和动态链 动态连接:A调用B时,B的活动记录中保存的A的活动记录地址 动态链:由动态连接组成的一个调用链
A call E; E call F; F call G; G call F; A ... E ... F ... G ... F ...
4. CALL P 的处理 (1)保存返回地址 D [ free ] := ip + 5 (2)保存主调过程的current D [free + 1] := current (3)建立P的current current := free (4)调整free free := free + L (5)转子 ip := P的代码段首地址
过程A中调用了P current 返回地址 动态连接 A的活动记录 free 返回地址 动态连接 即将建立的 P的活动记录
进入过程P以后 返回地址 动态链 A的活动记录 current 返回地址 动态链 P的活动记录 free
5. RETURN语句的处理 (1)恢复free free := current (2)恢复主调过程的current current := D[current + 1] (3)返回 ip := D [free]
二. 半动态变量的栈式分配 1. 活动记录内容 返回地址 动态链 静态链 保护区 参数单元 局部变量 临时变量 数组存储区 其中,局部变量中包括数组的描述符
2. 动态数组空间的分配 (1)编译时,分配内情向量表区,并产生在运行时动态建立内情向量和分配数组空间的指令; (2)一个单元激活后(进入该单元),遇到动态数组说明时,调用上述指令(填内情向量,分配数组空间),并调整free := free + L。
三. 动态变量的存储分配 在堆上进行存储分配
四. 非局部环境 非局部环境的引用必须考虑变量的作用域 1. 静态作用域规则——最近嵌套规则 (1)嵌套的层次 最外层单元为0层,若P是Q的直接外层,则Q的层次 = P的层次 + 1
unit A; y: int; unit B; y: int; unit C; unit D; …… end D; …... end C; …... end B;
unit E; x: int; unit F; z: int; A …… z:=x+y; B E …… end F; unit G; x,y: int; …... F G C D end G; …... end E; …... end A;
(2)最近嵌套规则 变量的作用域是指可访问该变量的程序范围 (I)变量x在单元U1中被说明,则x的作用域局部于U1,或称x在U1中是可见的; (II)变量x在U1中没有被说明,则x的作用域是包围U1的说明了x的最小外层单元。
2. 动态作用域规则 这是一种最近活动规则,对非局部变量,引用的应是最近外层中说明的。 动态作用域的最近外层指的是动态调用外层,如A-E-F-G-F的调用序列:当前F的调用外层为G。
五. 对非局部环境的引用 1. 静态作用域规则 (1)静态连接和静态链 静态连接:指向嵌套直接外层的最新活动记录的指针,它处于活动记录位移为8的存储单元中。 静态链:各嵌套程序单元的活动记录中,静态连接的序列构成一个静态链。
(2)非局部变量x的地址的求法 假设单元p中引用了单元t中的变量x,且p ,t的深度分别为np, nt。 设d = np – nt ,将x约束于(d , offset),问题的关键是找t的最新活动记录Dt,而Dt+ offset即为x的地址。
np – nt = 0: x为p中的局部变量 Dt = current • np – nt = 1: t是p的直接外层 Dt = D[current + 2] • np – nt = 2: t是p的再外层 Dt = D[D[current + 2] + 2] • np – nt = 3: t是p的再外层的外层 Dt = D[D[D[current + 2] + 2] + 2]