【发布时间】:2011-03-06 14:43:17
【问题描述】:
我决定我应该尝试实现协程(我认为我应该这样称呼它们)以获得乐趣和利润。我希望必须使用汇编程序,如果我想让它对任何事情真正有用,可能还需要一些 C。
请记住,这是出于教育目的。使用已经构建好的协程库太容易了(而且真的没有乐趣)。
你们知道setjmp 和longjmp 吗?它们允许您将堆栈展开到预定义的位置,并从那里恢复执行。但是,它不能倒退到堆栈上的“稍后”。只能早点回来。
jmpbuf_t checkpoint;
int retval = setjmp(&checkpoint); // returns 0 the first time
/* lots of stuff, lots of calls, ... We're not even in the same frame anymore! */
longjmp(checkpoint, 0xcafebabe); // execution resumes where setjmp is, and now it returns 0xcafebabe instead of 0
我想要的是一种无需线程即可在不同堆栈上运行两个函数的方法。 (显然,一次只运行一个。没有线程,我说过。)这两个函数必须能够恢复另一个的执行(并停止它们自己的)。有点像他们对对方是longjmping。一旦它返回到另一个函数,它必须从它离开的地方恢复(也就是说,在将控制权交给另一个函数的调用期间或之后),有点像 longjmp 返回到 setjmp 的方式。
我是这样想的:
- 函数
A创建并行堆栈并将其归零(分配内存等)。 - 函数
A将其所有寄存器推入当前堆栈。 - 函数
A将堆栈指针和基指针设置到该新位置,并压入一个神秘的数据结构,指示要跳转回的位置以及将指令指针设置回的位置。 - 函数
A将其大部分寄存器归零,并将指令指针设置为函数B的开头。
那是为了初始化。现在,以下情况将无限循环:
- 函数
B在该堆栈上工作,做它需要做的任何工作。 - 函数
B需要中断并再次给予A控制权。 - 函数
B将其所有寄存器推入堆栈,采用神秘的数据结构A一开始就给它,并将堆栈指针和指令指针设置为@ 987654337@告诉它。在此过程中,它交还A一个新的、修改后的数据结构,该结构告诉在哪里恢复B。 - 函数
A被唤醒,将其推入堆栈的所有寄存器弹回,并继续工作,直到它需要中断并再次给予B控制权。
这一切对我来说都很好。但是,有很多事情我并不完全放心。
- 显然,在好的 ol' x86 上,有这条
pusha指令会将所有寄存器发送到堆栈。然而,处理器架构不断发展,现在有了 x86_64,我们有了更多的通用寄存器,并且可能还有几个 SSE 寄存器。我找不到任何证据表明pusha确实推动了他们。现代 x86 CPU 中大约有 40 个公共寄存器。我必须自己完成所有pushes 吗?此外,没有用于 SSE 寄存器的push(尽管肯定会有一个等价物——我对整个“x86 汇编器”这件事很陌生)。 -
改变指令指针就这么简单吗?我可以像只是mov rip, rax(英特尔语法)那样做吗?此外,由于它不断变化,因此从中获取价值必须有些特殊。如果我喜欢mov rax, rip(又是英特尔语法),rip会定位在mov指令上,还是在它之后的指令上,或者介于两者之间?jmp foo。假人。 - 我曾多次提到神秘的数据结构。到目前为止,我假设它至少需要包含三件事:基指针、堆栈指针和指令指针。还有什么吗?
- 我是不是忘了什么?
- 虽然我真的很想了解事情是如何工作的,但我很确定有一些库可以做到这一点。你知道任何?是否有任何 POSIX 或 BSD 定义的标准方法,例如
pthread用于线程?
感谢您阅读我的问题文字墙。
【问题讨论】:
-
我记得上个学期在我的编程语言课程中读到了协程和仙人掌堆栈。只能说祝你好运,请不要把你的野心用于邪恶。
-
在 [3] 中:'59 6F 75 20 73 68 6F 75 6C 64 20 67 65 74 20 61 20 6C 69 66 65 2E 20 53 65 72 69 6F 75 73 6C 79 2E'。 (' ', '').decode('hex') Out[3]: '你应该得到一条生命。严重地。'呸呸呸
标签: c assembly stack x86-64 coroutine