这个玩意是我在做蒋岩炎老师的操作系统课的M2实验时,实现协程库的过程中所需要的一部分知识。为了更充分地深入地理解,我就参考大佬博客DIY抄袭了一个
参考文章:彻底理解setjmp/longjmp并DIY一个简单的协程
setjmp介绍
c语言有库函数setjmp()
与longjmp()
可以达到跨函数跳转的效果,其实它的原理就是setjmp()
保存了各种寄存器状态,后续调用longjmp()
来恢复寄存器来实现函数切换,像这样
1 |
|
输出结果
1 | a1 |
其实原理说起来简单,它的实现细节却很是繁杂,我理了好久,得理解[[汇编层面的函数调用]]
DIY版本的实现-save and restore
汇编版本
1 | .global save, restore |
写成c语言函数定义应该就是
1 | long save(struct context *cxt); |
c语言内嵌汇编版本
就是将上面的汇编改写一份为内嵌汇编版本的,虽然原理相同,还是有很多细节的不同
c语言内嵌汇编实现的函数,编译器会一开始就对%rbp
进行push,最后进行pop,如下图所示
那么这样就需要对save
函数保存寄存器的代码进行一些修改,对store
进行恢复时也要进行一些修改
save
函数,寄存器保存
因为有
1 | pushq %rbp |
这两句,导致现在的我们需要的%rsp
的值是原来的%rsp-16
得到的(两次push), 而%rbp
就存在%rsp
的位置,因为它是刚刚push
进去的,其他的没变,所以我们调整储存%rsp
和%rbp
的代码为
1 | leaq 16(%%rsp), %%rdx; \ |
restore函数,寄存器恢复
需要注意,因为多了一次push %rbp
和一次pop %rbp
,我们希望最后%rbp
%rip
和%rsp
的值是我们期望的值,我在中间加了push %%rdx
(里面装的%rip
的值),还有push %%rbp
来平衡两次pop
,以及让pop %rbq
后%rbq
里面是我想要的值
事实上,一开始就让%%rsp
的值-8,然后movq %rdx, (%rsp)
也是相同的效果
1 | movq (0)(%0), %%rsp ;\ |
测试
1 | /* test_saverestore.c */ |
输出:
1 | rsp : 0x7fffffffd770 |
perfect! 也许吧
- 本文作者: 汤圆
- 本文链接: https://littlesun.cloud/2023/07/27/DIY-setjmp/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!