移植luaCoco

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

内容简介:移植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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

企业应用架构模式

企业应用架构模式

Martin Fowler / 人民邮电出版社 / 2009 / 79.00元

随着信息技术的广泛应用,系统需要处理的数据量越来越大,企业级软件开发已经渐成主流,而开发人员面临的困难与挑战也是显而易见的。更糟糕的是,这一领域的资料一直非常缺乏。 本书是软件开发大师Martin Fowler的代表作,采用模式的形式系统总结了业界多年积累的经验,被称为“企业级应用开发领域的圣经”,出版以来一直畅销不衰,至今仍然无可替代。作 者在精彩地阐述了企业应用开发和设计中的核心原则基础......一起来看看 《企业应用架构模式》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具