我决定我应该尝试实现协程(我认为我应该这样称呼它们)以获得乐趣和利润。我希望必须使用汇编程序,如果我想让它对任何事情真正有用,可能还需要一些 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
我想要的是一种无需线程即可在不同堆栈上运行两个函数的方法。(显然,一次只运行一个。没有线程,我说过。)这两个函数必须能够恢复另一个的执行(并停止它们自己的)。有点像他们在longjmp
对对方说话。一旦它返回到另一个函数,它必须从它离开的地方恢复(也就是说,在将控制权交给另一个函数的调用期间或之后),有点像如何longjmp
返回到setjmp
.
我是这样想的:
- 函数
A
创建并行堆栈并将其归零(分配内存等)。 - 函数
A
将其所有寄存器推入当前堆栈。 - 函数
A
将堆栈指针和基指针设置到该新位置,并推送一个神秘的数据结构,指示在哪里跳转以及在哪里设置指令指针。 - 函数
A
将其大部分寄存器归零并将指令指针设置为函数的开头B
。
那是为了初始化。现在,以下情况将无限循环:
- 函数
B
在那个堆栈上工作,做它需要做的任何工作。 - 函数到了需要中断并再次给予控制
B
的地步。A
- 函数
B
将其所有寄存器推入堆栈,获取一开始给它的神秘数据结构A
,并将堆栈指针和指令指针设置为A
告诉它的位置。在这个过程中,它交还A
一个新的、修改过的数据结构,告诉在哪里恢复B
。 - 函数
A
被唤醒,将它压入堆栈的所有寄存器弹回,并继续工作,直到它需要中断并B
再次给予控制权。
这一切对我来说听起来不错。然而,有很多事情我并不完全放心。
- 显然,在好的 ol' x86 上,有这
pusha
条指令会将所有寄存器发送到堆栈。然而,处理器架构不断发展,现在有了 x86_64,我们有了更多的通用寄存器,并且可能还有几个 SSE 寄存器。我找不到任何pusha
确实推动他们的证据。现代 x86 CPU 中大约有 40 个公共寄存器。我必须自己做所有的push
es吗?此外,没有push
适用于 SSE 的寄存器(尽管肯定会有一个等价的——我对整个“x86 汇编器”这个东西是新手)。 改变指令指针就这么简单吗?我可以这样做只是mov rip, rax
(英特尔语法)吗?此外,由于它不断变化,因此从中获取价值必须有些特殊。如果我喜欢mov rax, rip
(再次使用 Intel 语法),将rip
定位在mov
指令上、指令之后,还是介于两者之间?jmp foo
。假的。- 我曾多次提到一种神秘的数据结构。到目前为止,我假设它至少需要包含三件事:基指针、堆栈指针和指令指针。还有别的事吗?
- 我忘了什么吗?
- 虽然我真的很想了解事情是如何工作的,但我很确定有一些库可以做到这一点。你知道任何?是否有任何 POSIX 或 BSD 定义的标准方法,比如
pthread
线程?
感谢您阅读我的问题文字墙。