我决定尝试执行协同程序(我认为这就是我应该如何称呼它们)以获得乐趣和利润。我希望必须使用汇编程序,如果我想让它对任何东西都有用,可能还需要一些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
。< / p>
这就是我的想法:
A
创建并归零并行堆栈(分配内存及所有内容)。A
将其所有寄存器推送到当前堆栈。A
将堆栈指针和基指针设置为该新位置,并按下神秘数据结构,指示要跳回的位置以及将指令指针放回的位置。 / LI>
A
将大多数寄存器清零,并将指令指针设置为函数B
的开头。这是初始化。现在,以下情况将无限循环:
B
适用于该堆栈,执行所需的任何工作。B
达到了需要中断并再次给予A
控制权的程度。B
将其所有寄存器推送到其堆栈,从最开始的神秘数据结构 A
给出它,并设置堆栈指针和指令指向A
告诉它的地方的指针。在此过程中,它会向A
提交一个新的,经过修改的数据结构,告知将在何处恢复B
。A
唤醒,弹回它推送到堆栈的所有寄存器,然后一直工作,直到它需要中断并再次给B
控制。这一切听起来都不错。但是,有很多事情我并不是很放心。
pusha
指令会将所有寄存器发送到堆栈。然而,处理器架构不断发展,现在使用x86_64,我们有了更多的通用寄存器,可能还有几个SSE寄存器。我找不到pusha
确实推动它们的任何证据。在现代的x86 CPU中有大约40个公共寄存器。我自己必须做所有push
es吗?此外,SSE寄存器没有push
(虽然必然是相同的 - 我对整个“x86汇编程序”的新东西)。mov rip, rax
(英特尔语法)吗?此外,从中获取价值必须有点特殊,因为它会不断变化。如果我确实喜欢mov rax, rip
(英特尔语法),rip
会将mov
指令放在jmp foo
指令之后,放在它之间的指令之间,还是介于两者之间?pthread
1}}。虚设。感谢您阅读我的问题 textwall。
答案 0 :(得分:9)
答案 1 :(得分:1)
良好的学习参考:libcoroutine,尤其是他们的setjmp / longjmp实现。我知道使用现有的库并不好玩,但你至少可以对你要去的地方有所了解。
答案 2 :(得分:1)
Simon Tatham有一个interesting implementation of coroutines in C,它不需要任何特定于架构的知识或堆栈摆弄。这不完全是你所追求的,但我认为它至少可能具有学术兴趣。
答案 3 :(得分:-1)