》有一次和女同学A吃饭,她带着小姑子一起吃。吃饭的时候同学给了我一块豆腐,小姑子一脸无辜的看着我眼神:笨叔叔,你除了占我妈贱,还占过谁的便宜,我无语了……”生活中,人们常说谁占了谁的便宜?电脑也能利用它吗?电脑里确实有很多便宜的“栈”,但不是“会计”,而是各种形式的“栈”。不知道你知道电脑里有多少个栈吗?比如:内核栈,中断栈,进程栈,线程栈,硬件栈,软件栈,还有一些人还在占坑。昨天我们讲了ARM32上的精彩或者高级的中断栈,今天继续讲“栈”。01什么是栈——先看看什么是栈?栈的英文叫做stack。stack中文是怎么解释的?1.存放货物或供旅客住宿的房屋:仓库|客栈。2、饲养牲畜的竹木围栏:马垛。那么在计算机中,它是什么呢?它实际上是一种存储数据的数据结构类型。一种特殊类型的线性列表,只能在一端插入和删除。它按照先进后出的原则存储数据。先进入的数据压入栈底,最后进入的数据在栈顶。当需要读取数据时,从栈顶弹出数据(最后的数据先读出)。看下图应该更清楚。这种数据结构的特点是后进先出(LIFO,LastInFirstOut),数据只能在字符串的一端压入(push)和弹出(pop)(称为:栈顶).向栈中存入数据称为PUSH,从栈中取出数据称为POP。大多数处理器架构都实现了硬件堆栈。有特殊的堆栈指针寄存器和特定的硬件指令来完成压入/弹出操作。比如在ARM架构上,R13(SP)指针是栈指针寄存器,PUSH是压栈的汇编指令,POP是出栈的汇编指令。我们经常听到有人说,栈,那个栈是什么鬼?是堆还是栈?其实stack本身就是一个stack,只是换了一个抽象的名字和马甲而已,有的人晕了。那堆是什么?在数据结构中,堆可以看作是一棵树,如:堆排序。在操作系统中,堆是操作系统中管理内存的一种方式。通常,程序员分配和释放它。如果程序员不释放它,它可能会在程序结束时被操作系统回收。分配方式类似于链表。栈,由操作系统自动分配和释放,存放函数参数值、局部变量值等。那么,堆、栈、栈,这三个容易混淆吗?02栈有什么用?—栈主要有两个作用,一个是函数调用,一个是进程调度。先说函数调用。我们知道函数调用需要注意什么吗?大家想到的可能是怎么传参数,怎么传函数返回值。在不同的计算机体系结构中,有不同的方法,但都必须在同一个地方使用堆栈。ARM和ARM64使用ATPCS(ARM-ThumbProcedureCallStandard/ARM-ThumbProcedureCallStandard)函数调用约定。对于ARM:参数1~参数4分别存放在R0~R3寄存器中,其余参数从右向左入栈,返回值存放在R0中。对于ARM64:参数1~参数8分别存放在X0~X7寄存器中,其余参数从右到左入栈,返回值存放在X0中。简而言之,栈将用于保存函数调用的参数。还有一件事需要保存,那就是局部变量。以ARM32为例,一个函数A调用另一个函数B的栈布局如下。fp寄存器(r11)和sp寄存器指向的区域称为栈帧。函数调用通常是嵌套的。同时,栈中会有多个函数的信息。每个未完成的函数都占据一个独立的连续区域,即栈帧。堆栈帧存储函数参数、局部变量和恢复先前堆栈帧所需的数据。如上图所示,假设当前运行在函数B中,当前fp和sp寄存器所指示的区域就是当前栈,栈帧B。当函数B返回到函数A时,sp构造的另一个栈保存在堆栈帧B中的fp寄存器是堆栈帧A。因此,堆栈是一个链接“帧”的列表,堆栈的每个块按地址降序分配。寄存器sp始终指向最新帧中使用的最低地址。ARM堆栈有点奇怪。其他CPU架构中提到的SP栈指针都是指向栈顶的,而ARM栈是自减栈,栈是向下增长的,即栈底在高地址,并且栈顶在低地址。有点奇怪和扭曲。堆栈的另一个用途是进程切换。每个进程都有自己的系统栈空间。这个栈空间指的是内核栈,是在fork时分配的。因此,当进程切换时,需要将前驱进程的上下文保存到前驱进程的内核栈中。在每个进程的生命周期中,势必会通过系统调用落入内核。执行系统调用落入内核后,这些内核代码使用的栈就不是原来进程用户空间中的栈,而是一个单独的内核空间中的栈,称为进程内核栈。运行在用户空间的进程需要栈,运行在内核空间的进程也需要栈,所以这些栈的定义是不一样的。下次我们再来跟大家聊聊哪些“叠”更便宜。最后一个问题,为什么CPU上电时第一条指令是汇编执行的?CPU能不能第一条指令就执行C语言代码?可以还是不可以?为什么?
