查了一天堆栈切换的资料,发现大家都在讲user stack 、kernel stack、interrupt stack、IST,但是没有人讲一下trampoline stack,这对初入kernel的小伙伴不太友好的trampoline stack,这里分享一下心得:
进程在执行的时候,必然会使用到系统调用或者中断,按照SDM中的描述,中断发生时,硬件会自动切换堆栈到内核堆栈:
这个切换后的堆栈的地址是存放在TSS中ESP0(或RSP0)中的:
问题是RSP0中存放的到底是哪个stack?
原本RSP0中存放的是thread 的kernel stack,这样进入中断或者INT指令后,就直接切换到thread的kernel stack上了,方便简洁,估计Intel原本的design就是这样的,但是!但是!后来情况不一样了,可能是某种安全因素,kernel引入了trampoline stack,这个trampoline stack的地址存放在RSP0上,把kernel stack的地址存放在了RSP1上,所以硬件每次自动切换堆栈的时候,从RSP0上读取到的是trampoline stack的地址,也就是跳转到了trampoline stack上,这个时候handler并没有工作在kernel stack上,还需要软件切换到kernel stack,而且还需要把硬件自动保存在trampoline stack上的register的值copy到kernel stack上,如果使用IRET指令返回,还需要把register恢复到trampoline stack上,因为IRET是从RSP0指向的stack上读取返回需要的register的值的。所以中断发生时,stack的切换过程如下:
user stack==》中断==》trampoline stack==》interrupt handler==》软件切换RSP到kernel stack
增加trampoline stack 的commit:https://lore.kernel.org/patchwork/patch/855083/
此时kernel stack 的地址是存放在RSP1上的,但是!后边又有人改了,把kernel stack的地址放在了TOP_OF_INIT_STACK: