变量究竟是存在寄存器还是堆栈?

【操作系统】堆栈寄存器sp详解以及栈帧 在x86架构中,函数调用的栈帧(Stack Frame)是函数调用过程中用于保存函数局部变量、返回地址、参数等的一块内存空间。堆栈寄存器是现代处理器的重要组成部分,在嵌入式开发之中,要对此区域的使用十分敏感,因为堆栈寄存器是处理器的重要组成部分,其高效管理对程序运行的稳定性和安全性起着关键作用。, SP)是处理器中一个专门用于管理栈(Stack)的寄存器,它保存了当前栈的顶部地址。接下来,为了进一步的理解堆栈中发生的事,我们使用x86汇编语言说明函数调用栈帧的创建和销毁的过程详情。 阅读详情
1. 变量是放在寄存器里还是堆栈里?

堆栈对于处理器来说就是一块内存区域,而寄存器是处理器触手可及的存储,对于RISC 处理器而言,堆栈中的数据CPU并不能直接进行运算,还是要先加载到寄存器中才行。对于编译器而言,我猜测还是优先会选择将变量用寄存器保存。那什么时候需要用到堆栈呢?什么东西需要保存到堆栈呢?一种是需要切换上下文的地方,另一种是需要传参的地方。函数调用就是一种典型应用。

2. 函数调用时的栈与寄存器

在这里插入图片描述
一个典型的函数调用流程如上图所示,关键的涉及栈和寄存器的步骤如下:

  1. 首先,在调用其他函数前,Caller需要保存自身的上下文,比如某个变量的值存在寄存器x1,该寄存器有可能在被调函数Callee中用到,那就需要存到堆栈中;
  2. 调用函数时的传参,参数的传递有两个选择,一个是将参数放到寄存器,另一个则是将参数放到堆栈中。
  3. Caller调用函数的时候需要将PC+4,即函数的返回地址存到寄存器或堆栈,被调函数执行完调到该返回地址执行;
  4. 对于Callee而言,首先要保存上文中的某些寄存器,有的同志可能会问,步骤1中Caller不是已经保存了一波寄存器了,为什么Callee中又来一波?以RISC-V为例,约定了两类寄存器——临时寄存器和保存寄存器,其中临时寄存器可以由Callee随意使用,所以就需要Caller来保存;而保存寄存器需要Callee来保证其值在调用前后不能改变,所以需要Callee存储。
  5. Callee在执行完之后,自然需要将“保存寄存器”的值恢复。此外,返回参数也需要存放至寄存器或堆栈中。

从上面的描述中可以看到,保存寄存器只能存到堆栈中,而保存参数和函数返回地址则既可以放在寄存器,也可以放在堆栈。RISC-V对此进行了如下约定:

寄存器名ABI名(编程用名)用途约定谁负责在函数调用过程中维护这些寄存器
x0zero读取时总为 0, 写入时不起任何效果N/A
x1ra存放函数返回值(return address)Caller
x2sp存放栈指针(stack pointer)Callee
x5~x7, x28~x31t0~t2, t3~t6临时(temporaries)寄存器,Callee 可能会使用这些寄存器,所以Callee 不保证这些寄存器中的值在函数调用过程中保持不变,这意味着对于 Caller 来说,如果需要的话,Caller 需要自己在调用 Callee 之前保存临时寄存器中的值。Caller
x8, x9, x18~x27s0, s1, s2~s11保存(saved)寄存器,Callee 需要保证这些寄存器的值在函数返回后仍然维持函数调用之前的原值,所以一旦 Callee 在自己的函数中会用到这些寄存器则需要在栈中备份并在退出函数时进行恢复。Callee
x10 , x11a0 , a1参数(argument)寄存器,用于在函数调用过程中保存第一个和第二个参数,以及在函数返回时传递返回值。Caller
x12 ~ x17a2 ~ a7参数(argument)寄存器,如果函数调用时需要传递更多的参数,则可以用这些寄存器,但注意用于传递参数的寄存器最多只有 8 个(a0 ~ a7),如果还有更多的参数则要利用栈。Caller

由于编译器做出了以上函数调用约定,这就意味着汇编指令中本来要带的参数不需要了,比如原来跳转并链接到某个label的指令是jal x1, offset,意思是跳转到 offset 制定位置,返回地址保存在 x1 (ra)中,现在由于做好了约定,可以直接使用伪指令jal offset来替代。

3. 函数调用实例

下面举个例子来说明一下小节2中的过程。
首先给出C语言代码如下,代码入口是_start函数,其中调用了aa_bb函数,aa_bb又调用了square函数。

void _start()
{
    // calling nested routine
    aa_bb(3, 4);
}

int aa_bb(int a, int b)
{
    return square(a) + square(b);
}

int square(int num)
{
    return num * num;
}

让我们根据2中的一些约定来写RISC-V汇编指令:

_start:
	la sp, stack_end	# 首先初始化堆栈指针
	li a0, 3			# 第一个参数放入寄存器a0
	li a0, 4			# 第二个参数放入寄存器a1
	call aa_bb			# Caller中没用到什么临时寄存器,所以不需要保存,直接跳转; call伪指令会将跳转地址放到寄存器ra中 
stop:
	j stop				# 死循环结束代码	

aa_bb:
	addi sp, sp, -16	# 栈指针初始化时指在栈去末尾,要存放4个word的数据,所以减16
	sw s0, 0(sp)		# 考虑到后面会用到保存寄存器,s0~s2,需要Callee进行保存
	sw s1, 4(sp)
	sw s2, 8(sp)
	sw ra, 12(sp)		# ra中目前存放的是_start函数下一条指令的地址,由于在aa_bb函数中还要调用别的函数,会覆盖掉ra寄存器的值,因此也要存下来
	
	mv s0, a0 			# a0寄存器的值挪到s0中
	mv s1, a1			# a1寄存器的值挪到s1中
	
	li s2, 0			# 将最终结果放在s2中,因此先将s2清零
	
	mv a0, s0			# 准备调用square函数啦,传参放入寄存器a0; 作为一个Caller没有需要保存的临时寄存器
	jal square			# 跳转到square函数中执行,返回地址会放在ra寄存器,覆盖了原来的ra
	add s2, s2, a0		# 计算的返回参数在a0中,加到s2上
	
	mv a1, s1			# 与上面计算同理
	jal square
	add s2, s2, a0
	
	mv a0, s2			# 最终的返回参数防止到a0寄存器
	lw s0, 0(sp)		# 从栈区中恢复各个保存寄存器的值
	lw s1, 4(sp)
	lw s2, 8(sp)
	lw ra, 12(sp)		# ra寄存器的值恢复回来
	addi sp, sp, 16	 	# 释放栈空间,栈指针依旧指向进入函数前的位置	
	ret

square:
	addi sp, sp, -8
	sw s0, 0(sp)
	sw s1, 4(sp)
	
	mv s0, a0
	mul s1, s0, s0
	mv a0, s1
	
	lw s0, 0(sp)
	lw s1, 4(sp)
	addi sp, sp, 8
	ret

stack_start:
	.rept 10			# 定义10个word大小的栈空间
	.word 0
	.endr
stack_end:

如果有人读了上面的代码,可能会发出一个疑问——为什么运算的时候非得用s0~s2这种保存寄存器,这不是脱裤子放屁呢吗?因为这个是教学演示代码,为了说明小节2中的函数调用流程。

4. volatile关键字的原理

volatile关键字存在的根本原因正是变量会存在寄存器还是内存中。假如一个变量会在多线程中用到,该变量如果在一个线程运行中被加载到了寄存器中,则显然后续的代码也会继续使用这个寄存器中的变量值,而不会使用内存中的该值。如果别的线程中的代码修改了内存中该变量的值,而本线性依旧使用寄存器中的值就会出错。
volatile就是告诉编译器,每次用到这个变量的时,都去内存中把这个值重新加载一次,而不是沿用之前的寄存器中的值。因为这个值可能被中途改变了。

参考

再次感谢并十分推荐来自PLCT实验室汪辰老师的课程《循序渐进、学习开发一个RISC-V上的操作系统》:https://www.bilibili.com/video/BV1Q5411w7z5?p=12&spm_id_from=pageDriver

图解说明Ollydbg寄存器与堆栈在恶意代码中的作用 通过图文详解Ollydbg中寄存器与堆栈的动态变化,揭示其在逆向分析恶意代码时的关键作用,帮助安全研究人员快速定位漏洞与执行逻辑。 阅读详情

相关推荐

【C语言基础】存储类型(自动变量、静态变量和寄存器变量)

但是,编译器并不一定要理睬register关键字,如果有太多的变量被声明为register,它只选取前几个实际存储于寄存器中,其余的就按普通自动变量处理。在程序执行到声明自动变量的代码块时,自动变量才被创建,当程序的执行流离开该代码块时,这些自动变量便自行销毁。在一个使用寄存器变量的函数返回之前,这些寄存器先前存储的值必须恢复,确保调用者的寄存器变量未被破坏。在代码块内部声明的变量的缺省存储类型是自动的(automatic),也就是说它存储于堆栈中,称为自动(auto)变量。

qq_40326539的博客 1665

【计算机基础】程序数据的5种存储位置(堆与栈) (转载)

存储位置

黄道婆的专栏 4722

内存分配及变量存储位置(堆、栈、方法区常量池、方法区静态区)(转)

程序运行时,有六个地方都可以保存数据:   1、 寄存器:这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。   2、 堆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中(new 出来的对象)。驻留于常规RAM(随机访问存储器)区域。但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会

隔壁老瓦的专栏 2790

堆栈与寄存器的区别

堆栈是内存的数据结构,内存是CPU和硬盘之间的通道。 而寄存器是CPU的存储器,速度快。

Black Mamba的博客 9087

Selenium ChromeDriver反爬与性能优化实战指南

ChromeDriver作为Selenium的核心驱动,本质是浏览器自动化执行引擎,其默认行为会暴露大量JS运行时指纹(如navigator.webdriver、window.chrome),极易被前端反爬系统识别;同时因进程管理粗放、渲染等待机制僵化,导致内存泄漏、僵尸进程和高并发下稳定性崩塌。本文围绕真实生产场景,系统解析浏览器指纹伪造原理、进程级资源管控策略、基于页面状态的智能等待范式,并提供Docker容器化、K8s弹性扩缩与Prometheus可观测性落地路径,助力开发者将ChromeDriver

weixin_29762151的博客 1259

iPad秒变Mac副屏的隐藏技巧:用BetterDisplay实现无线竖屏+90度锁定

本文详细介绍了如何利用BetterDisplay软件突破苹果原生“随航”(Sidecar)功能的限制,将iPad无线设置为Mac的竖屏副屏,并实现90度方向锁定。通过创建虚拟显示器、配置分辨率与旋转参数,并校准鼠标指针,用户可以高效利用iPad的纵向空间进行文档阅读、编码等工作,显著提升多屏协作效率。

weixin_29266647的博客 603

区分堆、栈内存里存放的变量

什么变量在堆内存里存放,什么变量在栈内存里存放 堆和栈的区别 (stack and heap) 一般认为在c中分为这几个存储区 1栈 - 有编译器自动分配释放 2堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 3全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束释放。 ...

weixin_40384363的博客 1940

ctftools-all-in-one使用说明

OAEP (Optimal Asymmetric Encryption Padding) 是一种用于RSA加密的填充方案,目的是提高RSA加密的安全性。在CTF比赛中,如果模数N的两个因子比较接近,使用"费马分解(N)"可能会更快地得到结果;OAEP 是 RSA 的一种标准填充方式(在 PKCS#1 v2.0+ 中定义),因此这类题目本质上仍是 RSA 题,但。,导致你不能直接用常规的 RSA 解密公式 �=�� mod �m=cdmodN 得到有意义的明文。如果你在 CTF 中遇到 RSA 题,

2501_91682449的博客 1713

C++基础编程第二章:寄存器、内存、堆栈

寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果,包括通用寄存器、专用寄存器和控制寄存器。其实寄存器就是一种常用的时序逻辑电路,但这种时序逻辑电路只包含存储电路。寄存器的存储电路是由锁存器或触发器构成的,因为一个锁存器或触发器能存储1位二进制数,所以由N个锁存器或触发器可以构成N位寄存器。寄存器是中央处理器内的组成部分。

goldenlake90的博客 1398

电源防反接保护电路全攻略实战A04

本文深入剖析了硬件设计中电源保护电路的关键技术与实战经验。文章指出,保护电路虽小但至关重要,通过对比分析二极管与MOSFET防反接方案,揭示了MOSFET在效率、温控方面的优势。针对过压保护,重点讲解了TVS二极管选型要点,强调钳位电压而非击穿电压才是保护核心。文章结合工业案例(如48V通信模块烧毁、5V系统MOSFET过热等),详细阐述了PCB布局规范、热设计路径等工程细节,并提供了USB接口ESD防护的完整选型方案。最后强调硬件设计必须考虑最坏工况,在成本与性能间找到平衡点,避免因小失大。

启芯硬件的博客 776

华为OD机试真题2026双机位C卷 C语言 实现【明日之星选举】

给定一组选票votes。votes[i]代表第 i 张选票的内容,包含一个字符串"zhangsan,hanmei",表示推荐zhangsan为明日之星,反对 hanmei 称为明日之星。赞成或反对使用逗号(,)分割。 给定一个数组n,表示最后将筛选出赞成票最多的n个人,如果赞成票相同,则反对票越少,排序越靠前;如果赞成票和反对票都相同,则按照姓名字典序升序排序。

MISAYAONE的博客 184

变量的存储类型

寄存器变量的创建、销毁时间和自动变量相同,但他需要一些额外的工作。在一个寄存器变量的函数返回前,这些寄存器先前存储的变量的值必须恢复,确保调用者的寄存器变量未被破坏。当函数开始执行时,它把需要使用的寄存器的内容保存到堆栈中,当函数返回时,这些值再复制回寄存器中。如果改代码块被数次执行,例如,一个函数被反复调用,则这些自动变量每次都将重新创建。在代码块再次执行时,这些自动变量在堆栈中所占据的内存位置有可能和原先的位置相同,也可能不同。,也就是不属于堆栈的内存,这类变量称为静态(static)变量。

2301_79989964的博客 486

中国工商银行企业架构建设实践最全总结

中国工商银行参考IBM持续价值创新方法并吸收TOGAF等主流企业架构的框架理念,结合业务实际情况形成工行自己的企业架构方法论并实践,为数字化转型提供支撑。本案例通过介绍中国工商银行企业架构之业务架构设计方法,为其他银行企业架构建设提供参考。 企业架构是数字化转型的抓手,可以帮助企业规划、管理和实施数字化解决方案,以实现业务目标并适应不断变化的市场条件。中国工商银行通过战略目标指导业务架构的规划和设计,业务架构建模后指导IT架构建模。

银行信息系统应用架构详解 2730

Crash 变量解析

这种情况比较常见,此时,局部变量必然位于某一级函数的堆栈中,该局部变量可能通过指针一级级向底层函数传递,所以可能位于多个函数的堆栈中,可以从不同的函数堆栈中解析。但解析会比较困难,难点在于难以确认相关变量在堆栈中的具体位置,解析方法很灵活,需要结合相关源代码,仔细分析流程,找到关键的点,更多的取决于分析者的经验和代码理解能力。在分析vmcore的过程中,常常需要解析内核某个流程中的关键变量的值,以便确认故障当时系统的状态,本文主要介绍变量的解析方法,主要分全局变量和局部变量两种情况。

RJ436的博客 827

SAP UI5 用数据绑定导出:从 ExportTypeCSV 到 Spreadsheet 的完整实践与取舍

SAP UI5 数据绑定导出机制通过复用 Model、Binding Path 等配置,将控件视图转换为文件格式。早期方案使用 sap/ui/core/util/Export 生成 CSV,需自定义 ExportType 扩展格式;现代方案推荐 sap/ui/export/Spreadsheet 直接导出 XLSX,支持类型精度和智能表格集成。注意 CSV 方案已弃用但仍有业务价值,需根据项目需求选择合适方案。两种方式都基于数据绑定,避免重复定义列结构和格式化逻辑。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 268

rk3568 Android12 默认授予预装app权限

默认授予预装应用权限可以避免用户在每次使用应用时都要重复同意权限请求。尽管在Android 12中,用户对权限的管理有了更高的控制权,但这也可能导致用户在频繁使用预装应用时感到繁琐和不必要的干扰。通过默认授权,减少了这种麻烦。一些厂商可能会根据其定制的Android系统(如Samsung的One UI、Xiaomi的MIUI)要求某些预装应用默认获得权限,以便应用能够提供厂商特定的功能。这也有助于设备厂商提供统一和稳定的服务,不至于因为权限问题导致某些功能无法使用。

weixin_35723192的博客 661

堆栈指针寄存器-SP详解

基本概念: 堆栈指针寄存器-SP是什么? 答:堆栈是计算机存储数据的一种数据结构,SP的作用就是指示当前要出栈或入栈的数据,并在操作执行后自动递增或递减。 至于是入栈递增还是入栈递减,就是由CPU的生产厂家确定,通常由一块存储区和指向该存储区的指针SP构成。  1. 在堆栈中保存数据叫入栈/压栈,从堆栈中读取数据叫出栈。 2.堆栈是一种具有“先进后出,后进先出”(LIFO--...

zcshh的博客 3万+

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

数据段、代码段、堆栈段、内存、堆、堆栈、CPU寄存器

1.函数代码存放在代码段。声明的类如果从未使用,则在编译时,会优化掉,其成员函数不占代码段空间。 全局变量或静态变量,放在数据段, 局部变量放在栈中, 用new产生的对象放在堆中, 内存分为4段,栈区,堆区,代码区,全局变量区 BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。 BSS是英文Block Started b

xungjhj的博客 2715

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

堆栈指针寄存器 SP详解以及栈的作用

本文引用地址:http://www.eepw.com.cn/article/201611/321963.htm 韦东山:https://blog.youkuaiyun.com/thisway_diy/article/details/76422943 堆栈是一种具有“后进先出”(LIFO---Last In First Out)特殊访问属性的存储结构。堆 栈一般使用RAM 物理资源作为存储体,再加上LI...

蓝色鲜橙多的博客 3万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR

在随机存储器区划出一块区域作为堆栈区,数据可以一个个顺序地存入(压入)到这个区域之中,这个过程称为‘压栈’(push )。通常用一个指针(堆栈指针 SP—StackPointer)实现做一次调整,SP总指向最后一个压入堆栈的数据所在的数据单元(栈顶)。从堆栈中读取数据时,按照堆栈 指针指向的堆栈单元读取堆栈数据,这个过程叫做 ‘弹出’(pop ),每弹出一个数据,SP 即向相反方向做一次调整,如此就实现了后进先出的原则。 堆栈是计算机中广泛应用的技术,基于堆栈具有的数据进出LIFO特性,常应用于保存中断断

zhuguanlin121的博客 1万+

静态变量、自动变量与寄存器变量的存储

一、我们声明的变量有几类?        变量的存储位置与变量的类型有关,这里变量的类型不是int、float之类的类型, 而是分为三类:静态变量、自动变量和寄存器变量。         然而,变量到底属于这三类中的哪一类是根据变量在源文件中的声明位置和声明方式所决定的。 二、这三类变量分别是怎么存储的(存储类型)?        变量的存储类型(storage class)是指存储变量值

迷雾江湖 的博客 1926

寄存器, 堆栈, 汇编指令详解

什么是寄存器? 内存区域 初识堆栈push和pop mov和lea jmp, call和retn以及汇编运算 浮点数汇编指令

感谢观看 902

ARM指针寄存器——堆栈指针寄存器SP、程序计数器PC、连接寄存器LR(1)

你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!” />你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!更多资料点击此处获qu!!

disrm84160的博客 2196

HardFault背后的内存战争:STM32堆栈溢出与寄存器防御工事

本文深入分析STM32开发中HardFault故障的根源,重点探讨堆栈溢出问题及Cortex-M内核通过SCB等寄存器构建的硬件防护机制。详细介绍了寄存器状态分析、堆栈溢出防御策略和高级调试技术,帮助开发者有效预防和诊断系统崩溃,提升嵌入式系统稳定性。

blue42的博客 849

嵌入式堆栈、ARM寄存器

ARM寄存器、嵌入式堆栈

Krysdong 807
上一篇: 编程与人生
下一篇: 内存管理01——链接脚本
隋边边
博客等级 码龄11年 304粉丝 91原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值