内容简介:移植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 处理器上。
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
看图解释一下代码:
- 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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 移植 Lua 到鸿蒙:首个移植成功的编程语言
- zeppelin 安装移植简述
- C 版本 MQTT 移植 Android
- 从Redis到Codis移植实践
- MTCNN移植安卓并检测视频中人脸
- Android QEMU 模拟器移植 - 编译
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!