greenlet是一个高效的python协程扩展,与libtask不同,由于greenlet是跑在 python虚拟机上的,而python虚拟机在操作系统上模拟了进程,线程与栈帧结构, 所以greenlet协程在做切换时必须同时切换python虚拟机的栈帧与操作系统进程 的栈帧.而这也是greenlet实现比较tricky的地方,要搞清楚greenlet的具体实 现机制,必须先搞清楚python的三个重要结构体:
PyFrameObject PyInterpreterState PyThreadState
先来看看PyFrameObject:
typedef struct _frame {
// PyFrameObject就是python虚拟机的栈帧结构,
// 也就是一段python的执行代码片,包括:
// 指向前一个栈帧的指针, 执行代码片地址,builtins dict, globals dict, locals dict
// 指向最后一个local地址的指针,指向栈顶地址的指针, 代码片所在线程的指针, 第一个local地址的指针
PyObject_VAR_HEAD
// f_back指向调用者的栈帧
struct _frame *f_back; /* previous frame, or NULL */
// 执行代码片
PyCodeObject *f_code; /* code segment */
PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
PyObject *f_globals; /* global symbol table (PyDictObject) */
PyObject *f_locals; /* local symbol table (any mapping) */
// f_valuestack指向最后一个local地址
PyObject **f_valuestack; /* points after the last local */
/* Next free slot in f_valuestack. Frame creation sets to f_valuestack.
Frame evaluation usually NULLs it, but a frame that yields sets it
to the current stack top. */
// f_stacktop指向栈顶, 当线程初始化时,f_stacktop和f_valuestack指向相同地址
PyObject **f_stacktop;
PyObject *f_trace; /* Trace function */
/* If an exception is raised in this frame, the next three are used to
* record the exception info (if any) originally in the thread state. See
* comments before set_exc_info() -- it's not obvious.
* Invariant: if _type is NULL, then so are _value and _traceback.
* Desired invariant: all three are NULL, or all three are non-NULL. That
* one isn't currently true, but "should be".
*/
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
PyThreadState *f_tstate;
int f_lasti; /* Last instruction if called */
/* Call PyFrame_GetLineNumber() instead of reading this field
directly. As of 2.3 f_lineno is only valid when tracing is
active (i.e. when f_trace is set). At other times we use
PyCode_Addr2Line to calculate the line from the current
bytecode index. */
int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and