作者回复: kdb_reboot,谢谢建议。这个建议不错,我麻烦编辑稍后加上。
作者回复: 👍,谢谢 chengzise 同学,谢谢补充。call 在调用的时候会做push eip的操作,而在ret的时候会做pop eip的操作。
作者回复: 这两个是在维护函数调用的栈帧。
指令地址本身的压栈和出栈是在 call 和 ret 的部分进行的。
你可以认为 call 的同时进行了一次 push rip 把PC寄存器里面的内容压栈了,而在 ret 的时候 pop 把这部分数据出栈写回到PC寄存器里面了。
作者回复: 这个是在维护栈帧,因为后面有两个临时变量需要在调用其他函数之前保留到栈里面。0x18是16进制的24
两个int各需要8 bit,一共16bit,然后ebp本来就要8bit,一共只有24bit,考虑对齐到16bit的整数倍还要额外的8bit一共24bit
作者回复: 如果是函数作用域内的临时变量,就是分配在栈上的啊。
首先Java运行时候的JVM自己就是一个应用程序,和C编译出来的机器码就不一样。
Java通过New出来的对象是在堆上,但是函数作用域里面的临时变量,以及对应的引用都是放在栈上的。
作者回复: 6个还是8个以内的调用参数会放在寄存器内而不是stack frame里面。
X64 下 stack 需要按 16 bytes 做 alignment 可能是导致你需要的空间变成 24 bytes 的原因。这里的 24 bytes 加上你的 8 bytes 的 rbp 正好是 32 bytes 能是16的倍数。
可以看看这个 stackoverflow 的问题 https://stackoverflow.com/questions/40580914/why-more-space-on-the-stack-frame-is-reserved-than-is-needed-in-x86
作者回复: 还需要考虑函数的调用参数传递哦。
作者回复: 是存到寄存器或者栈里面的,如果寄存器里面存不下,就会放到栈帧里面去。
多线程情况下,每个线程有自己的栈。但是线程切换是context switch,这个和函数调用并不相同。context switch通常要解决的是把当前现场中的其他信息保留下来,比如寄存器里面的内容等等,而不是做一次压栈。
作者回复: 其实是在call的时候会对PC寄存器进行压栈,做了一个push eip 的操作,而在ret的时候做了pop eip的操作。
作者回复: 每个线程都有一个自己的栈。
作者回复: 这两个是在维护函数调用的栈帧。
指令地址本身的压栈和出栈是在 call 和 ret 的部分进行的。
你可以认为 call 的同时进行了一次 push rip 把PC寄存器里面的内容压栈了,而在 ret 的时候 pop 把这部分数据出栈写回到PC寄存器里面了。
这一部分的确是有不少同学表示写得不够清楚,我晚点看单独会在FAQ里面更详细地写一下这个过程。也再修订一下这一讲希望能讲解地更清楚一些。
作者回复: java虚拟机其实是一个应用层的程序,java虚拟机的内部内存分配其实是在虚拟内存地址层面的分配。的确不涉及到操作系统和硬件层面的分页问题。
作者回复: 秋天同学你好,
我们在这里先要分清楚 抽象概念 和 实际的硬件实现部分。
寄存器 和 内存,是在硬件层面就是放在不同的位置,使用不同的物理硬件来实现的。
而栈是一个抽象概念,实际是存放在内存里面的。栈是用来管理函数调用的“现场”的。确保函数调用完成后,还能回到调用者那里。
作者回复: 小猪同学你好,
那么被调用的函数运行完之后,怎么知道要跳回到哪一个地址呢?
作者回复: 愤怒的虾干同学你好,
中断不是函数调用啊。不过中断的确会对cs,ip进行压栈,以及保存flags寄存器。但是函数调用虽然对于ip会进行压栈,但是并不需要取清理条件码寄存器,也就是flags寄存器的状态,也不需要对于cs进行压栈。