<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>assembly on liang2kl's Blog</title><link>https://yeshe.ng/blog/tags/assembly/</link><description>Recent content in assembly on liang2kl's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><managingEditor>liang2kl@outlook.com (Liang Yesheng)</managingEditor><webMaster>liang2kl@outlook.com (Liang Yesheng)</webMaster><copyright>from 2021, to infinity and beyond...</copyright><lastBuildDate>Thu, 26 May 2022 13:02:01 +0800</lastBuildDate><atom:link href="https://yeshe.ng/blog/tags/assembly/index.xml" rel="self" type="application/rss+xml"/><item><title>协程上下文切换</title><link>https://yeshe.ng/blog/posts/2022/05/context-switch/</link><pubDate>Thu, 26 May 2022 13:02:01 +0800</pubDate><author>liang2kl@outlook.com (Liang Yesheng)</author><guid>https://yeshe.ng/blog/posts/2022/05/context-switch/</guid><description>在用户态进行上下文切换</description><content>&lt;p>最近在研究 &lt;a href="https://github.com/PlatformLab/Arachne">Arachne&lt;/a> 做用户态线程调度的方法。&lt;/p>
&lt;p>众所周知，一个线程的状态包括：&lt;/p>
&lt;ul>
&lt;li>PC&lt;/li>
&lt;li>一些寄存器（即“上下文”）&lt;/li>
&lt;/ul>
&lt;p>切换线程时，需要切换上下文；为了将来能够恢复上下文，在切换时我们需要将当前的上下文保存在某个地方。我们尝试用 C 写一个简单的上下文切换。&lt;/p>
&lt;h2 id="上下文切换">上下文切换&lt;/h2>
&lt;p>进行上下文切换的汇编代码在上汇编课时就讲过：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">swapcontext:
# save current context
pushq %r12
pushq %r13
pushq %r14
pushq %r15
pushq %rbx
pushq %rbp
movq %rsp, (%rsi)
# restore previously saved context
movq (%rdi), %rsp
popq %rbp
popq %rbx
popq %r15
popq %r14
popq %r13
popq %r12
&lt;/code>&lt;/pre>&lt;p>上面的代码做了几件事情：&lt;/p>
&lt;ol>
&lt;li>保存当前上下文
&lt;ol>
&lt;li>将被调用者保存寄存器保存到当前的调用栈上&lt;/li>
&lt;li>将当前的栈指针 &lt;code>%rsp&lt;/code> 保存到 &lt;code>%rsi&lt;/code> 所指向的内存地址上&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>恢复之前保存的上下文
&lt;ol>
&lt;li>将栈指针恢复为 &lt;code>%rdi&lt;/code> 所保存的另一个协程的栈顶&lt;/li>
&lt;li>恢复另一个协程的栈上面保存的被调用者保存寄存器&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;p>这里需要理解一个问题：&lt;strong>为什么需要保存被调用者保存寄存器&lt;/strong>？&lt;/p>
&lt;p>“被调用者保存寄存器”指的是在调用子过程前后不发生改变的寄存器，即子过程需要在返回前将这些寄存器的值恢复。上面的 &lt;code>swapcontext&lt;/code> 是一个过程调用，因此需要遵循这一惯例。然而，与一般的过程调用不同，我们在这里实际上是切换到了另一个上下文中，因此我们实际上是恢复了另一个协程的上下文。&lt;/p>
&lt;p>这里可能会产生疑惑：调用者保存寄存器呢？实际上，调用 &lt;code>swapcontext&lt;/code> 的过程在调用它之前就已经把调用者保存寄存器保存好了（如果有必要的话）。理解的关键是：&lt;code>swapcontext&lt;/code> 是一个过程，而不是内联的汇编代码。&lt;/p>
&lt;h2 id="调用栈结构">调用栈结构&lt;/h2>
&lt;p>由于 &lt;code>swapcontext&lt;/code> 中会将当前的上下文保存到某个内存地址，我们之后只需要将这个内存地址传入 &lt;code>swapcontext&lt;/code> 就可以切换回来了。在这里，我们主要考虑在一开始如何切换到一个指定的函数上；我们需要初始化调用栈，使得 &lt;code>swapcontext&lt;/code> 后能够跳转到指定的函数。&lt;/p>
&lt;p>不难画出这样的一个调用栈：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | Return Address |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | Registers |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sp-&amp;gt; | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们将需要切换到的函数的地址保存在 &lt;code>Return Address&lt;/code> 中。在使用 &lt;code>popq&lt;/code> 指令恢复所有保存的寄存器后，&lt;code>rsp&lt;/code> 指向此函数地址，&lt;code>swapcontext&lt;/code> 返回（&lt;code>ret&lt;/code> 指令）后便跳转到函数的入口地址。需要注意的是 &lt;code>Return Address&lt;/code> 上方需要保留 8 字节的空间。&lt;/p>
&lt;p>栈的初始化过程如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define SPACE_FOR_SAVED_REGISTERS 48UL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define STACK_SIZE 1024UL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">routine2&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#f92672">*&lt;/span>sp2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">char&lt;/span> stack[&lt;span style="color:#ae81ff">8&lt;/span> &lt;span style="color:#f92672">+&lt;/span> SPACE_FOR_SAVED_REGISTERS &lt;span style="color:#f92672">+&lt;/span> STACK_SIZE];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Initialize stack for routine2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> sp2 &lt;span style="color:#f92672">=&lt;/span> stack &lt;span style="color:#f92672">+&lt;/span> SPACE_FOR_SAVED_REGISTERS &lt;span style="color:#f92672">+&lt;/span> STACK_SIZE &lt;span style="color:#f92672">-&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">sizeof&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#f92672">*&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Set return address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#f92672">*&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#f92672">**&lt;/span>)sp2 &lt;span style="color:#f92672">=&lt;/span> routine2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Point to bottom of the register stack
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> sp2 &lt;span style="color:#f92672">=&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>)sp2 &lt;span style="color:#f92672">-&lt;/span> SPACE_FOR_SAVED_REGISTERS;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">//...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这里，我们创建一个栈，并将 &lt;code>routine2&lt;/code> 的地址写到相应位置；&lt;code>sp2&lt;/code> 指向最后一个储存寄存器值的位置。&lt;/p>
&lt;p>此时，&lt;code>stack&lt;/code>、&lt;code>sp2&lt;/code> 和调用栈的结构如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | Address of `routine2` |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | Registers |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sp2-&amp;gt; | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | Stack Space |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>stack-&amp;gt; | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-----------------------+
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="程序">程序&lt;/h2>
&lt;p>最后，我们写几个简单的上下文切换的 demo。&lt;/p>
&lt;h3 id="例子-1">例子 #1&lt;/h3>
&lt;p>在这里，我们进行两次上下文切换，从 &lt;code>routine&lt;/code> 切换到 &lt;code>routine2&lt;/code>，再切换回 &lt;code>routine&lt;/code>。&lt;/p>
&lt;p>其中，&lt;code>swapcontext&lt;/code> 需要加上 &lt;code>noinline&lt;/code> 的 attribute，这是为了保证 &lt;code>swapcontext&lt;/code> 作为过程被调用。&lt;/p>
&lt;p>另外，编译时需要加上 &lt;code>-fomit-frame-pointer&lt;/code>，否则编译器在编译时会在过程前后分别加上 &lt;code>pushq %rbp&lt;/code> 和 &lt;code>popq %rbp&lt;/code>，导致栈结构遭到破坏。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define SPACE_FOR_SAVED_REGISTERS 48UL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define STACK_SIZE 1024UL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">__attribute__&lt;/span>((noinline)) swapcontext(&lt;span style="color:#66d9ef">void&lt;/span>&lt;span style="color:#f92672">**&lt;/span> saved, &lt;span style="color:#66d9ef">void&lt;/span>&lt;span style="color:#f92672">**&lt;/span> target) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">asm&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;pushq %r12&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;pushq %r13&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;pushq %r14&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;pushq %r15&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;pushq %rbx&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;pushq %rbp&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;movq %rsp, (%rsi)&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">asm&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;movq (%rdi), %rsp&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;popq %rbp&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;popq %rbx&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;popq %r15&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;popq %r14&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;popq %r13&lt;/span>&lt;span style="color:#ae81ff">\n\t&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;popq %r12&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#f92672">*&lt;/span>sp2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#f92672">*&lt;/span>sp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">char&lt;/span> stack[&lt;span style="color:#ae81ff">8&lt;/span> &lt;span style="color:#f92672">+&lt;/span> SPACE_FOR_SAVED_REGISTERS &lt;span style="color:#f92672">+&lt;/span> STACK_SIZE];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">routine&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;routine()&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Switch to routine2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> swapcontext(&lt;span style="color:#f92672">&amp;amp;&lt;/span>sp2, &lt;span style="color:#f92672">&amp;amp;&lt;/span>sp);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;routine() end&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">routine2&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;routine2()&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Switch back
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> swapcontext(&lt;span style="color:#f92672">&amp;amp;&lt;/span>sp, &lt;span style="color:#f92672">&amp;amp;&lt;/span>sp2);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Unreachable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> printf(&lt;span style="color:#e6db74">&amp;#34;routine2() end&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Initialize stack for routine2
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> sp2 &lt;span style="color:#f92672">=&lt;/span> stack &lt;span style="color:#f92672">+&lt;/span> SPACE_FOR_SAVED_REGISTERS &lt;span style="color:#f92672">+&lt;/span> STACK_SIZE &lt;span style="color:#f92672">-&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">sizeof&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#f92672">*&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Set return address
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#f92672">*&lt;/span>(&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#f92672">**&lt;/span>)sp2 &lt;span style="color:#f92672">=&lt;/span> routine2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Point to bottom of the register stack
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> sp2 &lt;span style="color:#f92672">=&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">*&lt;/span>)sp2 &lt;span style="color:#f92672">-&lt;/span> SPACE_FOR_SAVED_REGISTERS;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> routine();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;main routine&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>routine()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine() end
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main routine
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="例子-2">例子 #2&lt;/h3>
&lt;p>继续尝试一些其他的用法。在这里，我们计算 0 累加至 9 的结果，并在每次循环时都切换一次上下文，然后立刻切换回来。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">routine&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;routine()&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">int&lt;/span> sum &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; i &lt;span style="color:#f92672">&amp;lt;&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span>; i&lt;span style="color:#f92672">++&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sum &lt;span style="color:#f92672">+=&lt;/span> i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> swapcontext(&lt;span style="color:#f92672">&amp;amp;&lt;/span>sp2, &lt;span style="color:#f92672">&amp;amp;&lt;/span>sp);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;sum: %d&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, sum);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">routine2&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> (;;) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> printf(&lt;span style="color:#e6db74">&amp;#34;routine2()&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> swapcontext(&lt;span style="color:#f92672">&amp;amp;&lt;/span>sp, &lt;span style="color:#f92672">&amp;amp;&lt;/span>sp2);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>routine()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>routine2()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sum: 45
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="汇编结果">汇编结果&lt;/h3>
&lt;p>使用 &lt;code>-O1&lt;/code> 编译上面第二个例子（去掉所有 &lt;code>printf&lt;/code>），&lt;code>routine&lt;/code> 的汇编代码如下：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-assembly" data-lang="assembly">0000000000000639 &amp;lt;routine&amp;gt;:
639: 55 push %rbp
63a: 53 push %rbx
63b: bb 0a 00 00 00 mov $0xa,%ebx
640: 48 8d 2d f9 09 20 00 lea 0x2009f9(%rip),%rbp # 201040 &amp;lt;sp&amp;gt;
647: 48 89 ee mov %rbp,%rsi
64a: 48 8d 3d 47 0e 20 00 lea 0x200e47(%rip),%rdi # 201498 &amp;lt;sp2&amp;gt;
651: e8 af ff ff ff callq 605 &amp;lt;swapcontext&amp;gt;
656: 83 eb 01 sub $0x1,%ebx
659: 75 ec jne 647 &amp;lt;routine+0xe&amp;gt;
65b: 5b pop %rbx
65c: 5d pop %rbp
65d: c3 retq
&lt;/code>&lt;/pre>&lt;p>可以看到，&lt;code>for&lt;/code> 循环使用 &lt;code>%ebx&lt;/code>（被调用者保存）进行计数，在调用 &lt;code>swapcontext&lt;/code> 前后并没有保存其值。保存这个寄存器是 &lt;code>swapcontext&lt;/code> 的责任。&lt;/p></content></item></channel></rss>