如何创建并行堆栈并在其上运行协同程序?

时间:2010-06-22 02:24:57

标签: c assembly stack x86-64 coroutine

我决定尝试执行协同程序(我认为这就是我应该如何称呼它们)以获得乐趣和利润。我希望必须使用汇编程序,如果我想让它对任何东西都有用,可能还需要一些C.

请记住,这是出于教育目的。使用已经构建的协程库太容易了(而且真的没什么乐趣)。

你们知道setjmplongjmp吗?它们允许您将堆栈展开到预定义位置,并从那里继续执行。但是,它无法回退到堆栈上的“稍后”。只能早点回来。

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>

这就是我的想法:

  1. 函数A创建并归零并行堆栈(分配内存及所有内容)。
  2. 函数A将其所有寄存器推送到当前堆栈。
  3. 函数A将堆栈指针和基指针设置为该新位置,并按下神秘数据结构,指示要跳回的位置以及将指令指针放回的位置。 / LI>
  4. 函数A将大多数寄存器清零,并将指令指针设置为函数B的开头。
  5. 这是初始化。现在,以下情况将无限循环:

    1. 函数B适用于该堆栈,执行所需的任何工作。
    2. 功能B达到了需要中断并再次给予A控制权的程度。
    3. 函数B将其所有寄存器推送到其堆栈,从最开始的神秘数据结构 A给出它,并设置堆栈指针和指令指向A告诉它的地方的指针。在此过程中,它会向A提交一个新的,经过修改的数据结构,告知将在何处恢复B
    4. 函数A唤醒,弹回它推送到堆栈的所有寄存器,然后一直工作,直到它需要中断并再次给B控制。
    5. 这一切听起来都不错。但是,有很多事情我并不是很放心。

      • 显然,在好的'x86上,有这条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}}。虚设。
      • 我曾多次提到神秘的数据结构。到目前为止,我假设它至少需要包含三件事:基指针,堆栈指针和指令指针。还有别的吗?
      • 我忘了什么吗?
      • 虽然我真的很想理解如何运作,但我很确定有少数几个库可以做到这一点。你知道任何?是否有任何POSIX或BSD定义的标准方法,如线程的{{1}}?

      感谢您阅读我的问题 textwall。

4 个答案:

答案 0 :(得分:9)

答案 1 :(得分:1)

良好的学习参考:libcoroutine,尤其是他们的setjmp / longjmp实现。我知道使用现有的库并不好玩,但你至少可以对你要去的地方有所了解。

答案 2 :(得分:1)

Simon Tatham有一个interesting implementation of coroutines in C,它不需要任何特定于架构的知识或堆栈摆弄。这不完全是你所追求的,但我认为它至少可能具有学术兴趣。

答案 3 :(得分:-1)

boost.org上的boost.coroutine(boost.context)为你做了一切