首页 > 其他分享 >《深入理解计算机系统》学习(4):过程(如函数)和栈存储

《深入理解计算机系统》学习(4):过程(如函数)和栈存储

时间:2024-03-26 20:29:22浏览次数:24  
标签:计算机系统 存储 调用 函数 地址 指令 参数 寄存器 过程

目录

过程

过程中是软件中一种很重要的抽象,提供了一种封装代码的方式,用一组指定的参数和一个可选的返回值实现了某种功能。然后,可以在程序的不同的地方使用这个功能。设计良好的软件用过程作为抽象机制,隐藏某个行为的具体实现,同时又提供清晰简洁的接口定义,说明要计算的是哪些值,过程会对程序状态产生什么样的影响。不同的编程语言中,过程的形式多样:函数、方法、子例程、处理函数等,但是它们有一些共有的特性。

假设过程P调用过程Q,Q执行后返回到P。这些动作包括下面一个或多个机制:

  • 传递控制,在进入过程Q的时候,程序计数器必须被设置为Q的代码的起始地址,然后在返回时,要把程序计数器设置为P中调用Q后面那条指令的地址。
  • 传递数据,P必须能够向Q提供一个或多个参数,Q必须能够向P返回一个值。
  • 分配和释放内存,在开始时,Q可能需要为局部变量分配空间,而在返回前,又必须释放这些空间。

1 运行时栈

C语言过程调用机制的一个关键特性在于使用了栈数据结构提供的后进先出的内存管理原则。程序可以使用栈来管理过程所需要的存储空间,栈和程序寄存器存放着传递控制和数据、分配内存所需要的信息,当P调用Q时,控制和数据信息添加到栈顶,当Q返回时,这些信息会释放掉。

当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间。这个部分称为过程的栈帧。当函数满足两个条件时不需要分配栈帧:(1)该函数不会调用其他函数,(2)所有局部变量都可以保存在寄存器中。

请添加图片描述
当前正在执行的过程的栈帧总是在栈顶。当过程P调用过程Q时,会把返回地址压入栈中,指明当Q返回时,要从P程序的哪个位置继续执行。返回地址作为P的栈帧的一部分,存放的是与P相关的状态。Q的代码会拓展当前栈的边界,分配栈帧所需的空间。在这个空间中可以保存寄存器的值,分配局部变量空间,为调用过程设置参数。通过寄存器,过程P最多可以传递6个整数值(也就是指针和整数),但是如果Q需要更多的参数,P可以在调用Q之前在自己的栈帧中存储这些参数。

2 转移控制

将控制从函数P转移到函数Q只需要简单地把程序计数器设置为Q的代码的起始位置。从Q返回时,处理器必须记录需要继续P的执行的代码位置。

在x86-64机器中,指令call Q调用过程Q,记录起始位置。该指令将地址A(返回地址)压入栈中,并将PC设置为Q的起始位置。ret指令会从栈中弹出地址A,并将PC设置为A。请添加图片描述
图为两个函数topleaf的反汇编代码,以及在main函数中调用top处的代码。main中的callq指令将栈指针%rsp的值设置为该指针后的mov指令地址 0x400560,程序计数器的值设置为top的起始指令地址 0x400545
在这里插入图片描述

类似的,top中的callq指令将栈指针%rsp的值设置为该指针后的add指令地址 0x40054e,程序计数器%rip的值设置为leaf函数的起始指令地址0x400540。而leaf函数中的retq指令弹出add指令地址(调用leaf函数的返回地址)0x40054e,栈指针%rsp的值变为栈中弹出指令的上一条指令地址,即mov指令地址(调用top函数的返回地址) 0x400560 ,程序计数器%rip的值设置add指令地址 0x40054e

3 数据传送

在x86-64中,大部分过程间的数据传送是通过寄存器实现的。当过程P调用过程Q时,P的代码必须首先把参数复制到适当的寄存器中,参数在寄存器%rdi%rsi和其他寄存器中传递。当从Q返回到P时,P的代码可以访问寄存器%rax中的返回值。

x86-64中,可以通过寄存器最多传递6个整型(如整数和指针)参数,根据参数在参数列表中的顺序分配寄存器。寄存器使用的名字取决于要传递的数据类型的大小,可以通过64位寄存器适当的部分访问小于64位的参数。例如,如果第一个参数是32位的,那么可以用%edi来访问。
在这里插入图片描述
如果一个函数有大于6个整型参数,超出6个的部分就要通过栈来传递。假设调用过程Q,有 n 个整型参数,且 n > 6,那么P的代码分配的栈帧必须要能容纳7到 n 号参数的存储空间。也就是把参数7~ n 放到栈(P的栈帧)上,参数7位于栈顶。参数准备完成后,程序才执行call指令将控制转移到过程Q。
在这里插入图片描述

4 栈上局部存储

在部分情况下,局部数据必须放在内存中,常见的情况包括:

  • 寄存器不足够存放所有的本地数据
  • 对一个局部变量使用地址运算符&,因此必须能够产生一个地址
  • 某些局部变量是数组或结构,因此必须能够通过数组或结构引用或被访问到

一般来说,过程通过减小栈指针在栈上分配空间,分配的结果作为栈帧的一部分,标号为局部变量。

图3-32是一个必须在栈上分配局部变量存储空间的函数示例。
在这里插入图片描述
在这里插入图片描述
call_proc的汇编代码从第2行到第15行都是为调用proc做准备,其中包括为局部变量(第3行到第6行)和函数参数(第7行到第15行)建立栈帧,将函数参数加载至寄存器(第10行到第15行)。x1、x2、x3、x4为局部变量,首先在栈帧中为四个局部变量分配空间,然后将前六个函数参数(x1、&x1、x2、&x2、x3、&x3)加载到寄存器,后两个函数参数x4、&x4存放在栈空间。最后,调用过程proc。
在这里插入图片描述

标签:计算机系统,存储,调用,函数,地址,指令,参数,寄存器,过程
From: https://blog.csdn.net/qq_45088122/article/details/136963463

相关文章

  • C++的format函数
    C++20引入了<format>库,它提供了类似于Python的格式化字符串的功能。你可以使用std::format函数来格式化字符串。format   格式控制标记  [[fill]align][sign][#][0][wigth][precision][type]下面依次讲解format的格式控制标记的基本用法:1.[[fill]align]这个格式......
  • 05-python函数进阶和文件
    函数函数的多返回值变量间逗号隔开,可以多种类型defhello():return1,True,"hello"x,y,z=hello()函数的传参方式位置参数,根据位置传参defcan1(x,y,z):print(x,y,z)can1(2,4,8)关键字参数,根据k=v传参,可以和位置参数混用,位置参数需要在前defcan2......
  • Oracle数据库入门第三课(函数)
    前面二白讲了一些简单的查询语句,仅仅知道查询语句的语法是不够的,要想实现更多的需求,更重要的是函数的使用,这节课我们简单说一下一些函数的使用。一、函数的分类什么叫做函数?函数就是用来实现某种功能的,提前声明好的代码块分类:•系统函数         ‣单行函数......
  • 上传图片前端使用base64数据格式展示,后端数据库存储二进制文件
    添加时上传图片upload.render({elem:'#docImg',url:Feng.ctxPath+'/doctor/upload'//改成您自己的上传接口,before:function(obj){//预读本地文件示例,不支持ie8obj.preview(function(index,file,result){......
  • Python函数式编程自带函数
    一.map函数需求1:num1=[1,2,3,4],我的需求是把num1中的每个元素平方后组成新列表。ret=[]num1=[1,2,3,4]foriinnum1:ret.append(i**2)print(ret)#输出结果:>>>[1,4,9,16]需求2:如果有1万个列表呢,怎么办?思路:如果用for循环,当然功能上是没问题的,但是需要......
  • 调用SQL存储过程
    ImportsSystem.Data.SqlClient PublicSubExecuteStoredProcedure()'定义连接字符串,应该根据实际情况修改DimconnectionStringAsString="Server=你的服务器地址;Database=你的数据库名;UserId=你的用户名;Password=你的密码;" '创建连接对象UsingconnectionA......
  • 【RTE里Rte_DRead函数和Rte_Read函数在代码里的区别】
    前言使用RTE时,常会使用到Rte_DRead和Rte_Read函数,今天以代码视角看看二者的区别。应用层在应用层调用Rte_DRead接口函数读取CAN信号值。read_isSignalA=Rte_DRead_Isignal_isSignalA_Isignal_isSignalA();在应用层调用Rte_Read接口函数读取CAN信号值。Rte_Read......
  • 【C语言】Infiniband驱动__mlx4_init_one函数
    一、注释Linux内核驱动程序中的部分,属于Mellanox网卡驱动mlx4的初始化过程。//Mellanox以太网驱动主程序代码staticint__mlx4_init_one(structpci_dev*pdev,intpci_dev_data,structmlx4_priv*priv){interr;//错误码变量intnvfs[ML......
  • 函数是什么?C++函数详解!
    1、函数的声明和定义在复杂的程序中,如果全部的代码都写在main函数中,main函数体将非常庞大臃肿。把任务分工到其它的函数中,main函数只负责程序的核心流程,具体的任务由其它函数完成。这种思想就是模块化编程。声明和定义函数的语法:返回值的数据类型函数名(参数一的数据类型......
  • Pandas基础之drop函数的使用
    drop方法的用法:drop(labels,axis=0,level=None,inplace=False)        -- axis为0时表示删除行,axis为1时表示删除列参数说明:labels:显示要删除的行或列的标签名axis:axis=0时按行删除,axis=1时按列删除level:代表标签所在的索引级别,一般不使用inplace:值为......