移植luaCoco

栏目: C · 发布时间: 7年前

内容简介:移植luaCoco

2017-05-30 13:38 PM

lua 语言最大的卖点之一就是他的协程(coroutine)了。但是在 lua 5.1 中有一个文档都没提到的一个坑:协程 只能 在 lua 中使用,当调用 yield 时,如果当前的调用栈上有 c 代码,则会报错 “attempt to yield across metamethod/C-call boundary” 。目前有个第三方的 patch 叫做 luaCoco 可以让 lua 支持 “真协程”。本文研究了 luaCoco 的内部实现,并把它移植到了 xtensa 处理器上。

移植luaCoco

setjmp 的实现

在文章的开头需要先讲解一下 c语言 标准库中 setjmp 的内部实现,因为之后的 luaCoco 的实现就是对 setjmp 的数据结构的一个 hack。

先看看 setjmp 的用法

#inlcude <stdio.h>
#include <stdlib.h>
#inlcude <setjmp.h>

int main() {
 jmp_buf buf;
 int code = setjmp(buf);
 if (code == 0) {
 print("before jmp\n");
 longjmp(buf, 1024);
 } else {
 printf("jmp here, code: %d\n", code);
 }
 return 0;
}

在调用 setjmp 的时候,会返回 0,从而会执行 if (code == 0) { 为 true 的 block,会打印出 before jmp 。当执行了 longjmp 之后,程序的执行会重新跳转到 setjmp 那一行(第七行)然而这次 setjmp 的返回值 code 不再是 0,而是 longjmp 的第二个参数(1024),这样就会打印出 jmp here, code: 1024

利用 setjmp 的这个功能能实现出很多有趣的东西,比如在c语言中做 Exception ,但是 setjmp 是怎么实现的呢?我们去读一读源码。

libc 的实现有好多种,常见的比如 glibc、uclibc 和 musl-libc,但是我们这次读一读 newlib 的源码。newlib 也是一个 libc 的实现,常用于嵌入式开发中。

打开 inlcude/machine/setjmp-dj.h 文件,可以看到 jmp_buf 的定义:

// from inlcude/machine/setjmp-dj.h
typedef struct {
 unsigned long eax;
 unsigned long ebx;
 unsigned long ecx;
 unsigned long edx;
 unsigned long esi;
 unsigned long edi;
 unsigned long ebp;
 unsigned long esp;
 unsigned long eip;
} jmp_buf[1];

发现这个结构体是用来存储 cpu 的寄存器的。这里很好理解,因为要实现 长跳转(longjmp),必须要首先把跳转的目的地的 现场 先保存下来。

setjmp 函数做的工作就是保存现场:

// from machine/i386/setjmp.S
 /*
 ** jmp_buf:
 ** eax ebx ecx edx esi edi ebp esp eip
 ** 0 4 8 12 16 20 24 28 32
 */

 #include "i386mach.h"

 .global SYM (setjmp)
 .global SYM (longjmp)
 SOTYPE_FUNCTION(setjmp)
 SOTYPE_FUNCTION(longjmp)

SYM (setjmp):

 pushl ebp
 movl esp,ebp

 pushl edi
 movl 8 (ebp),edi

 movl eax,0 (edi)
 movl ebx,4 (edi)
 movl ecx,8 (edi)
 movl edx,12 (edi)
 movl esi,16 (edi)

 movl -4 (ebp),eax
 movl eax,20 (edi) // edi

 movl 0 (ebp),eax
 movl eax,24 (edi) // ebp

 movl esp,eax
 addl $12,eax
 movl eax,28 (edi) // esp

 movl 4 (ebp),eax
 movl eax,32 (edi) // eip (PC)

 popl edi
 movl $0,eax
 leave
 ret
移植luaCoco setjmp 的栈帧

看图解释一下代码:

  • 17 行:push ebp 到栈上(esp 同时下移 4 字节)
  • 18 行:ebp 指向 esp
  • 20 行:push edi 到栈上(esp 同时再下移 4 字节)
  • 21 行:8(ebp) 保存的是 jmp_buf 的指针,先把它放到 edi 中
  • 23 - 27 行:分别把 eax、ebx、ecx、edx、esi 保存到 jmp_buf 里
  • 29 - 30 行:-4 (ebp) 是edi,如图
  • 32 - 33 行:(ebp) 就是之前的ebp,如图
  • 35 - 37 行:保存 esp
  • 39 - 40 行:如图,保存 return addr 到 jmp_buf->eip

longjmp 的实现是正好相反的:

SYM (longjmp):
 pushl ebp
 movl esp,ebp

 movl 8(ebp),edi /* get jmp_buf */
 movl 12(ebp),eax /* store retval in j->eax */
 testl eax,eax
 jne 0f
 incl eax
0:
 movl eax,0(edi)

 movl 24(edi),ebp

 __CLI
 movl 28(edi),esp

 pushl 32(edi) 

 movl 0(edi),eax
 movl 4(edi),ebx
 movl 8(edi),ecx
 movl 12(edi),edx
 movl 16(edi),esi
 movl 20(edi),edi
 __STI

 ret

代码就不赘述了,基本上就是恢复 现场 、把longjmp的第二个参数作为返回值返回(7 - 9 行还有一个判断:如果参数为0的话,会把它改成 1)。

luaCoco 的实现

// TODO: 写完它

xtensa 架构 ABI

// TODO: 写完它

移植 luaCoco

// TODO: 写完它

以上所述就是小编给大家介绍的《移植luaCoco》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Introduction to Graph Theory

Introduction to Graph Theory

Douglas B. West / Prentice Hall / 2000-9-1 / USD 140.00

For undergraduate or graduate courses in Graph Theory in departments of mathematics or computer science. This text offers a comprehensive and coherent introduction to the fundamental topics of graph ......一起来看看 《Introduction to Graph Theory》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具