写点什么

程序的机器级表示 - 控制

用户头像
引花眠
关注
发布于: 2020 年 08 月 30 日

过程简介

这里所谓的过程在其他语言中可能叫做函数、方法或子例程等。它包括一组参数和一个可选的返回值。而一个过程的调用包括将传递参数,控制权转移,接收返回值。而如果要在机器层级支持过程调用,同样需要执行这些逻辑,包括传递控制、传递参数、分配和释放内存等。



运行时栈帧

大部分语言使用的是栈来进行内存管理的,使用栈的好处就是很容易进行内存的申请和释放 x86-64体系中,栈底在内存中高地址,栈顶在低地址,所以栈顶指针减小就可以申请内存,增加就可以释放内存。 所以程序可以使用栈来管理他的过程需要的存储空间,栈和寄存器存储着传递控制和数据、分配内存需要的的信息。 c语言的运行时机制,当c语言进行过程调用使用的栈 当过程P调用过程Q时,P及向上追溯到P的调用链都挂起,控制和数据信息添加到栈顶,当Q运行时,可以通过移动栈顶的位置分配释放内存。



当程序需要的存储空间大于寄存器提供的空间时,就需要在内存中为其分配空间,这个分配的空间就是过程的栈帧(stack fram), 其作用是传递参数、存储返回信息、保存寄存器、局部存储等。



所以当P调用Q时会把返回地址压入栈中,用于指明当Q返回后,P从哪个位置开始运行。这个返回地址属于P的栈帧范围。



当过程P传递的参数超过6个时,超过的部分需要使用栈来进行传递,这些参数保存在P的栈帧中。 当不需要传递很多参数时,直接使用寄存器既可以满足要求。甚至过程本身都可以不分配栈帧,直接使用寄存器就可以完成。



转移控制

将控制从P移动到Q只需要将PC寄存器的地址设置为Q代码的起始地址,不过为了从Q返回之后继续执行,需要记录P继续执行代码的位置。 在x86-64机器中,这个信息使用指令call Q调用过程Q时记录的。 该指令会把A地址(返回地址,紧跟在call指令之后那条指令的地址)在P的栈帧,并将PC寄存器的地址设置为Q代码的起始地址。 ret指令与call指令的作用正好相反,会把A指令从栈中弹出,并将PC的值设置为A的值。



call指令有一个目标指明被调用的过程的起始地址



数据传送

在过程调用时,除了要对转移控制也需要对数据进行传递,包括调用参数的传入和调用结果的返回。 参数传递时,首先使用的是寄存器,当寄存器不够时,才使用栈进行数据传递(这些参数属于调用者的栈帧): 使用寄存器的顺序有特殊的要求:



  1. 约定规则 %rdi %rsi %rdx %rcx %r8 %r9

  2. 其他的参数则按照顺序压入栈



栈上的局部数据

大部分情况下不需要使用超出寄存器大小的本地存储区域,但是一些情况下需要将数据保存在内存中,包括:



  1. 寄存器不够存放本地数据

  2. 对一个本地变量使用&运算,因此必须产生一个地址

  3. 某些本地变量是数据或结构



寄存器中的局部数据

不同于栈帧,寄存器是每个过程都会用到的共享资源,所以为了保证过程的正确调用,需要对寄存器中的值进行保护。 对于x86-64来说,有一组统一的寄存器使用惯例,所有程序必须遵循(约定大于实现)。 被调用者保存的寄存器:%rbx,%rbp和%r12~%r15,两种保存方式 不去使用这些寄存器 将该寄存器的值保存到栈帧,过程返回之前恢复 其他寄存器(除了%rsp),都是调用者保存



递归的过程

栈非常适合实现递归,每个过程都有自己的栈帧用来保存自己的数据



参考资料

  1. 豆瓣-深入理解计算机系统(原书第3版)



发布于: 2020 年 08 月 30 日阅读数: 41
用户头像

引花眠

关注

还未添加个人签名 2018.06.11 加入

还未添加个人简介

评论

发布
暂无评论
程序的机器级表示-控制