基本上读完了greenlet的源代码,代码不多,就2000行C语言的代码,其中有一部分栈寄存器的修改的代码是由汇编实现的。。。
一句话来说明greenlet的实现原理:通过栈的复制切换来实现不同协程之间的切换。。。
那么接下里来具体的来看看greenlet的代码到底是怎么实现的。。。
好了,先来看看greenlet对象对应的C语言结构体:
- /**
- States:
- stack_stop == NULL && stack_start == NULL: did not start yet
- stack_stop != NULL && stack_start == NULL: already finished
- stack_stop != NULL && stack_start != NULL: active
- **/
- //greenlet对象最终对应的数据的C结构体,这里可以理解为python对象的属性
- typedef struct _greenlet {
- PyObject_HEAD
- char* stack_start; //栈的顶部 将这里弄成null,标示已经结束了
- char* stack_stop; //栈的底部
- char* stack_copy; //栈保存到的内存地址
- intptr_t stack_saved; //栈保存在外面的大小
- struct _greenlet* stack_prev; //栈之间的上下层关系
- struct _greenlet* parent; //父对象
- PyObject* run_info; //其实也就是run对象
- struct _frame* top_frame; //这里可以理解为主要是控制python程序计数器
- int recursion_depth; //栈深度
- PyObject* weakreflist;
- PyObject* exc_type;
- PyObject* exc_value;
- PyObject* exc_traceback;
- PyObject* dict;
- } PyGreenlet;
这里stack_start,stack_stop就是用于分别指向当前这个协程的栈的顶部与栈的底部,而且可以通过这几个标志位的值来判断当前这个greenlet对象的状态。。。
另外这里可以看到parent指针,用这个来构成greenlet对象的层次关系。。。
接下来来看看与Python对象之间对应关系:
- //这个是定义的暴露给python的greenlet对象,它以greenlet.h里面定义的_greenlet位基础,然后指定了方法,创建方法等
- PyTypeObject PyGreenlet_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "greenlet.greenlet", /* tp_name */
- sizeof(PyGreenlet), /* tp_basicsize */ //指定对象的大小,其实也就是结构体的大小
- 0, /* tp_itemsize */
- /* methods */
- (destructor)green_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- 0, /* tp_repr */
- &green_as_number, /* tp_as _number*/
- 0, /* tp_as _sequence*/
- 0, /* tp_as _mapping*/
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | GREENLET_GC_FLAGS, /* tp_flags */
- "greenlet(run=None, parent=None) -> greenlet\n\n"
- "Creates a new greenlet object (without running it).\n\n"
- " - *run* -- The callable to invoke.\n"
- " - *parent* -- The parent greenlet. The default is the current "
- "greenlet.", /* tp_doc */
- (traverseproc)GREENLET_tp_traverse, /* tp_traverse */
- (inquiry)GREENLET_tp_clear, /* tp_clear */
- 0, /* tp_richcompare */
- offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- green_methods, /* tp_methods */ //对象方法的定义
- 0, /* tp_members */
- green_getsets, /* tp_getset */ //属相方法
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- offsetof(PyGreenlet, dict), /* tp_dictoffset */ //这个对象的dict的偏移
- (initproc)green_init, /* tp_init */ //初始化 这里主要是设置run以及parent
- GREENLET_tp_alloc, /* tp_alloc */ //内存分配,其实也就是分配sizeof(PyGreenlet)的大小
- green_new, /* tp_new */ //构造函数 这里会将parent默认的设置为全局的greenlet
- GREENLET_tp_free, /* tp_free */ //释放
- (inquiry)GREENLET_tp_is_gc, /* tp_is_gc */ //gc
- };
这个里面构造以及初始化函数都提供了,那么先来看看构造函数吧,当在python中执行环境中创建greenlet对象的时候,将会先调用green_new方法,接着调用green_init方法。。。
- //创建一个greenlet对象,这个可以理解为greenlet的构造函数
- //这里会先将parent默认指向当前环境所属的greenlet对象
- //如果在greenlet对象的构造函数中带有parent对象,那么待会在init中会设置
- static PyObject* green_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
- {
- PyObject* o;
- if (!STATE_OK)
- return NULL;
- //先用基本对象来构造
- o = PyBaseObject_Type.tp_new(type, ts_empty_tuple, ts_empty_dict);
- if (o != NULL) { //这里默认将parent设置为ts_current,也就是在哪一个greenlet环境里面创建的,那么新创建的greenlet的parent就是所属的环境greenlet
- Py_INCREF(ts_current);
- ((PyGreenlet*) o)->parent = ts_current;
- }
- return o;
- }
这个代码很好理解吧,先构造父类,接着设置当前greenlet的默认parent,这里可以看到将parent默认的设置为当前环境所属的greenlet对象,也就是说假如我们在A中创建另外一个greenlet B,而且构造函数中没有传递parent参数,这个并没有关系,因为默认就已经将B的parent设置为A了...
好了,接下来来看看初始化函数:
- //初始化greenlet对象,这个可以理解为在构造之后,将会调用这个方法来进行初始化,主要是检查设置run,以及设置parent
- //这里可能没有parent参数,不过也没有关系,因为在构造的时候就先设置为创建环境的greenlet
- static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwargs)
- {
- PyObject *run = NULL;
- PyObject* nparent = NULL;
- //看当前这两个东西是否存在
- static char *kwlist[] = {"run", "parent", 0};
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:green", kwlist,
- &run, &nparent))
- return -1;
- if (run != NULL) { ///这里run对象是必须有的
- //设置run对象
- if (green_setrun(self, run, NULL))
- return -1;
- }
- //这里在初始化的时候可以没有parent
- if (nparent != NULL && nparent != Py_None)
- //如果没有指定parent对象,那么将跟对象设置,在这个里面将会进行parent链是否存在环的情况进行判断
- return green_setparent(self, nparent, NULL);
- return 0;
- }
这里初始化函数主要是设置构造的时候传递进来的执行函数,也就是run对象,然后就如果有传递parent的话,将会将这个greenlet对象的parent设置为传递进来的greenlet对象。。。。。
接下来来看看greenlet对象对应的一些方法的定义:
- //这个就是暴露给greenlet对象的的方法了
- static PyMethodDef green_methods[] = {
- {"switch", (PyCFunction)green_switch, //switch方法的定义
- METH_VARARGS | METH_KEYWORDS, green_switch_doc},
- {"throw", (PyCFunction)green_throw, METH_VARARGS, green_throw_doc},
- {"__getstate__", (PyCFunction)green_getstate, METH_NOARGS, NULL},
- {NULL, NULL} /* sentinel */
- };
这里就可以看到最为重要的switch方法其实对应的green_switch方法,。。嗯,那么接下来就来对照着如下python代码来分析greenlet吧:
- from greenlet import greenlet
- def test1(name):
- print name
- gr2.switch()
- print "over"
- def test2():
- print "test-2"
- return "test2-fjsfjs"
- gr1 = greenlet(test1)
- gr2 = greenlet(test2)
- value = gr1.switch("fjsfjs")
- print value
首先,得要先来看看greenlet模块的初始化:
- PyMODINIT_FUNC
- initgreenlet(void)
- #endif
- {
- PyObject* m = NULL;
- char** p = NULL;
- PyObject *c_api_object;
- static void *_PyGreenlet_API[PyGreenlet_API_pointers];
- GREENLET_NOINLINE_INIT();
- #if PY_MAJOR_VERSION >= 3
- m = PyModule_Create(&greenlet_module_def);
- #else
- m = Py_InitModule("greenlet", GreenMethods); //初始化greenlet模块,这里其实定义的方法比较的少,getcurrent啥的
- #endif
- if (m == NULL)
- {
- INITERROR;
- }
- //为模块添加版本信息
- if (PyModule_AddStringConstant(m, "__version__", GREENLET_VERSION) < 0)
- {
- INITERROR;
- }
- #if PY_MAJOR_VERSION >= 3
- ts_curkey = PyUnicode_InternFromString("__greenlet_ts_curkey");
- ts_delkey = PyUnicode_InternFromString("__greenlet_ts_delkey");
- #if GREENLET_USE_TRACING
- ts_tracekey = PyUnicode_InternFromString("__greenlet_ts_tracekey");
- ts_event_switch = PyUnicode_InternFromString("switch");
- ts_event_throw = PyUnicode_InternFromString("throw");
- #endif
- #else
- ts_curkey = PyString_InternFromString("__greenlet_ts_curkey");
- ts_delkey = PyString_InternFromString("__greenlet_ts_delkey");
- #if GREENLET_USE_TRACING
- ts_tracekey = PyString_InternFromString("__greenlet_ts_tracekey");
- ts_event_switch = PyString_InternFromString("switch");
- ts_event_throw = PyString_InternFromString("throw");
- #endif
- #endif
- if (ts_curkey == NULL || ts_delkey == NULL)
- {
- INITERROR;
- }
- if (PyType_Ready(&PyGreenlet_Type) < 0)
- {
- INITERROR;
- }
- PyExc_GreenletError = PyErr_NewException("greenlet.error", NULL, NULL);
- if (PyExc_GreenletError == NULL)
- {
- INITERROR;
- }
- #if PY_MAJOR_VERSION >= 3 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5)
- PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit",
- PyExc_BaseException, NULL);
- #else
- PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit",
- NULL, NULL);
- #endif
- if (PyExc_GreenletExit == NULL)
- {
- INITERROR;
- }
- ts_empty_tuple = PyTuple_New(0);
- if (ts_empty_tuple == NULL)
- {
- INITERROR;
- }
- ts_empty_dict = PyDict_New();
- if (ts_empty_dict == NULL)
- {
- INITERROR;
- }
- //这里可以理解为创建root greenlet对象
- ts_current = green_create_main(); //首先将ts_current创建为main greenlet
- if (ts_current == NULL)
- {
- INITERROR;
- }
- Py_INCREF(&PyGreenlet_Type);
- //为当前的模块添加属性
- PyModule_AddObject(m, "greenlet", (PyObject*) &PyGreenlet_Type); //这个是比较重要的,也就是greenlet的构造结构
- Py_INCREF(PyExc_GreenletError);
- PyModule_AddObject(m, "error", PyExc_GreenletError);
- Py_INCREF(PyExc_GreenletExit);
- PyModule_AddObject(m, "GreenletExit", PyExc_GreenletExit);
- PyModule_AddObject(m, "GREENLET_USE_GC", PyBool_FromLong(GREENLET_USE_GC));
- PyModule_AddObject(m, "GREENLET_USE_TRACING", PyBool_FromLong(GREENLET_USE_TRACING));
- /* also publish module-level data as attributes of the greentype. */
- for (p=copy_on_greentype; *p; p++) {
- PyObject* o = PyObject_GetAttrString(m, *p);
- if (!o) continue;
- PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o);
- Py_DECREF(o);
- }
- /*
- * Expose C API
- */
- /* types */
- _PyGreenlet_API[PyGreenlet_Type_NUM] = (void *) &PyGreenlet_Type;
- /* exceptions */
- _PyGreenlet_API[PyExc_GreenletError_NUM] = (void *) PyExc_GreenletError;
- _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void *) PyExc_GreenletExit;
- /* methods */
- _PyGreenlet_API[PyGreenlet_New_NUM] = (void *) PyGreenlet_New;
- _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] =
- (void *) PyGreenlet_GetCurrent;
- _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void *) PyGreenlet_Throw;
- _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void *) PyGreenlet_Switch;
- _PyGreenlet_API[PyGreenlet_SetParent_NUM] =
- (void *) PyGreenlet_SetParent;
- #ifdef GREENLET_USE_PYCAPSULE
- c_api_object = PyCapsule_New((void *) _PyGreenlet_API, "greenlet._C_API", NULL);
- #else
- c_api_object = PyCObject_FromVoidPtr((void *) _PyGreenlet_API, NULL);
- #endif
- //为当前的模块创建C O=API
- if (c_api_object != NULL)
- {
- PyModule_AddObject(m, "_C_API", c_api_object);
- }
- #if PY_MAJOR_VERSION >= 3
- return m;
- #endif
- }
代码挺长的,不过其实最重要的是要做两件事情:
(1)创建默认的最顶层的greenlet对象。。。而且将ts_current这个全局变量指向它
(2)在当前模块中加入greenlet对象的namespace吧。。。(也不知道该咋说)
这里先来看看这个初始化创建的顶层的greenlet对象是怎么创建的吧:
- //创建顶层greenlet对象,栈底部为-1,顶部为1
- static PyGreenlet* green_create_main(void)
- {
- PyGreenlet* gmain; //待会创建的顶层的greenlet的引用
- //创建一个字典对象
- PyObject* dict = PyThreadState_GetDict();
- if (dict == NULL) {
- if (!PyErr_Occurred())
- PyErr_NoMemory();
- return NULL;
- }
- /* create the main greenlet for this thread */
- //为当前线程创建greenlet对象
- gmain = (PyGreenlet*) PyType_GenericAlloc(&PyGreenlet_Type, 0);
- if (gmain == NULL)
- return NULL;
- gmain->stack_start = (char*) 1; //栈顶部为1
- //因为无符号,所以-1其实是最大的,这里在栈的复制拷贝的时候,就可以保证这里是最原始的,因为栈是由高地址向低地址扩展的
- gmain->stack_stop = (char*) -1; //栈底部为-1 因为char*是无符号的,所以可以保证这个是最大的
- gmain->run_info = dict;
- Py_INCREF(dict);
- return gmain;
- }
这里其实最终要的就是设置了两个值,首先是stack_stop栈底指针。。设置成了-1,因为char*本身是无符号的,所以其实这里就保证了stack_stop其实设置的时最大值。。。这个很重要。。因为要明白,栈是由高地址向低地址扩展的。。然后就还设置了stack_start栈顶指针。。。
好了,greenlet模块的初始化完成了,那么接下来开始来分析python代码:
- gr1 = greenlet(test1)
创建了一个greenlet对象,传入了一个run函数,也就是test1函数,根据前面看到的构造和初始化函数可以知道,当前gr1的parent将会指向最开始初始化建立的greenlet对象。。。
接着看python代码:
- gr2 = greenlet(test2)
这里创建了greenlet对象,名字叫gr2,运行函数是test2,嗯,同理,它的parent也是初始化建立的greenlet对象。。。
接着看代码:
- value = gr1.switch("fjsfjs")
这里调用gr1的switch方法,那么接下来就开始进入C语言的范围了。。我们来看看它的实现吧:
- //greenlet对象的switch方法其实就是对应到这个方法的
- //其实最终调用的时g_switch方法
- //第一个参数是执行switch的greenlet对象,第二参数是参数数组,第三个是关键字参数
- static PyObject* green_switch(
- PyGreenlet* self,
- PyObject* args,
- PyObject* kwargs)
- {
- Py_INCREF(args);
- Py_XINCREF(kwargs);
- //调用g_switch方法来进行栈的切换
- return single_result(g_switch(self, args, kwargs));
- }
这里有3个参数,self就是调用switch方法的green let对象了,args是参数列表,kwargs是关键字参数。。。
这里调用了g_switch方法来接下里执行。。
- //在python层面中,调用了greenlet的switch方法,其实是最终调用这个来进行执行的
- //这里target参数就是执行switch的greenlet对象
- //(1)先获取运行信息,参数啥的(2)从切换到的greenlet开始,找到一个可以运行的greenlet对象,通过parent向上遍历
- //(3)如果这个greenlet已经是active了,那么直接调用g_switchstack方法,如果这个greenlet还没有启动,那么需要g_initialstub进行初始化
- static PyObject *
- g_switch(PyGreenlet* target, PyObject* args, PyObject* kwargs)
- {
- /* _consumes_ a reference to the args tuple and kwargs dict,
- and return a new tuple reference */
- int err = 0;
- PyObject* run_info;
- /* check ts_current */
- if (!STATE_OK) { //如果状态不对,那么这里需要减少参数的引用计数
- Py_XDECREF(args);
- Py_XDECREF(kwargs);
- return NULL;
- }
- run_info = green_statedict(target); //获取运行信息
- if (run_info == NULL || run_info != ts_current->run_info) { //检查一下运行信息,万一是null咋办
- Py_XDECREF(args);
- Py_XDECREF(kwargs);
- PyErr_SetString(PyExc_GreenletError, run_info
- ? "cannot switch to a different thread"
- : "cannot switch to a garbage collected greenlet");
- return NULL;
- }
- ts_passaround_args = args; //保存switch传进来的参数
- ts_passaround_kwargs = kwargs; //保存switch传进来的关键字参数
- /* find the real target by ignoring dead greenlets,
- and if necessary starting a greenlet. */
- //找到真正需要指定的greenlet
- while (target) { //需要通过parent引用向上遍历,只要找到一个可用的就停止
- if (PyGreenlet_ACTIVE(target)) { //找到一个可用的greenlet
- ts_target = target; // 指向运行的greenlet
- err = g_switchstack(); //切换到这个栈,里面会调用slp_switch方法,用汇编实现
- break;
- }
- if (!PyGreenlet_STARTED(target)) { //如果都没有启动,那么需要初始化
- //这个变量挺有意思的
- void* dummymarker; //这里创建了一个新的局部变量,其实是通过它的地址待会来分割新创建的栈,用其来做新栈的底部
- ts_target = target;
- err = g_initialstub(&dummymarker); //对于没有启动的协程,这里需要进行初始化
- if (err == 1) {
- continue; /* retry the switch */
- }
- break;
- }
- target = target->parent;
- }
- /* For a very short time, immediately after the 'atomic'
- g_switchstack() call, global variables are in a known state.
- We need to save everything we need, before it is destroyed
- by calls into arbitrary Python code. */
- //这里处理switch返回参数,这里
- //这里可以看出来,其实switch的返回值也是通过ts_passaround_args,ts_passaround_kwargs这两个全局变量来传递的
- args = ts_passaround_args;
- ts_passaround_args = NULL;
- kwargs = ts_passaround_kwargs;
- ts_passaround_kwargs = NULL;
- if (err < 0) { //如果在栈的切换的时候出现了错误
- /* Turn switch errors into switch throws */
- assert(ts_origin == NULL);
- Py_CLEAR(kwargs);
- Py_CLEAR(args);
- } else {
- PyGreenlet *origin;
- #if GREENLET_USE_TRACING
- PyGreenlet *current;
- PyObject *tracefunc;
- #endif
- origin = ts_origin;
- ts_origin = NULL;
- #if GREENLET_USE_TRACING
- current = ts_current;
- if ((tracefunc = PyDict_GetItem(current->run_info, ts_tracekey)) != NULL) {
- Py_INCREF(tracefunc);
- if (g_calltrace(tracefunc, args ? ts_event_switch : ts_event_throw, origin, current) < 0) {
- /* Turn trace errors into switch throws */
- Py_CLEAR(kwargs);
- Py_CLEAR(args);
- }
- Py_DECREF(tracefunc);
- }
- #endif
- Py_DECREF(origin);
- }
- /* We need to figure out what values to pass to the target greenlet
- based on the arguments that have been passed to greenlet.switch(). If
- switch() was just passed an arg tuple, then we'll just return that.
- If only keyword arguments were passed, then we'll pass the keyword
- argument dict. Otherwise, we'll create a tuple of (args, kwargs) and
- return both. */
- if (kwargs == NULL) //没有关键字参数,那么直接返回参数列表
- {
- return args;
- }
- else if (PyDict_Size(kwargs) == 0)
- {
- Py_DECREF(kwargs);
- return args;
- }
- else if (PySequence_Length(args) == 0)
- {
- Py_DECREF(args);
- return kwargs;
- }
- else //两种参数都有
- {
- PyObject *tuple = PyTuple_New(2);
- if (tuple == NULL) {
- Py_DECREF(args);
- Py_DECREF(kwargs);
- return NULL;
- }
- PyTuple_SET_ITEM(tuple, 0, args);
- PyTuple_SET_ITEM(tuple, 1, kwargs);
- return tuple;
- }
- }
这里target参数也就是执行了switch方法的green let对象,对应到python代码,也就是我们建立的gr1对象。。
一个while循环。。。
这里需要注意,由于我们的gr1并没有开始运行,所以将会执行如下代码:
- //这个变量挺有意思的
- void* dummymarker; //这里创建了一个新的局部变量,其实是通过它的地址待会来分割新创建的栈,用其来做新栈的底部
- ts_target = target;
- err = g_initialstub(&dummymarker); //对于没有启动的协程,这里需要进行初始化
先是在当前栈里面创建了一个dummymarker对象,然后将ts_target这个全局变量指向gr1,然后调用g_initialstub方法来初始化,并且将dummymarker的地址也传进去了。。。这个参数很重要,因为它将被设置成gr1的栈底指针。。。
好了,来看看g_initialstub方法:
- //对于刚刚创建,都还没有启动的greenlet对象,第一调用switch方法的时候将会调用这里进行初始化
- static int GREENLET_NOINLINE(g_initialstub)(void* mark)
- {
- int err;
- PyObject *o, *run;
- PyObject *exc, *val, *tb;
- PyObject *run_info;
- PyGreenlet* self = ts_target;
- PyObject* args = ts_passaround_args; //获取switch传进来的参数
- PyObject* kwargs = ts_passaround_kwargs; //switch传进来的关键字参数
- /* save exception in case getattr clears it */
- PyErr_Fetch(&exc, &val, &tb);
- /* self.run is the object to call in the new greenlet */
- run = PyObject_GetAttrString((PyObject*) self, "run"); //获取run属性
- if (run == NULL) {
- Py_XDECREF(exc);
- Py_XDECREF(val);
- Py_XDECREF(tb);
- return -1;
- }
- /* restore saved exception */
- PyErr_Restore(exc, val, tb);
- /* recheck the state in case getattr caused thread switches */
- if (!STATE_OK) {
- Py_DECREF(run);
- return -1;
- }
- /* recheck run_info in case greenlet reparented anywhere above */
- run_info = green_statedict(self);
- if (run_info == NULL || run_info != ts_current->run_info) {
- Py_DECREF(run);
- PyErr_SetString(PyExc_GreenletError, run_info
- ? "cannot switch to a different thread"
- : "cannot switch to a garbage collected greenlet");
- return -1;
- }
- /* by the time we got here another start could happen elsewhere,
- * that means it should now be a regular switch
- */
- if (PyGreenlet_STARTED(self)) {
- Py_DECREF(run);
- ts_passaround_args = args;
- ts_passaround_kwargs = kwargs;
- return 1;
- }
- /* start the greenlet */
- self->stack_start = NULL; //设置栈的顶部为null
- self->stack_stop = (char*) mark; //设置栈底地址,其实这个地址是从当前运行栈里面新创建了一个变量来获取的
- //这里设置栈之间的层次关系
- if (ts_current->stack_start == NULL) { //如果当前环境greenlet环境栈不在,那么向上设置一层
- /* ts_current is dying */
- self->stack_prev = ts_current->stack_prev;
- } else {
- self->stack_prev = ts_current; //一般情况下就将栈层次设置为当前环境所属的greenlet
- }
- //初始化当前栈的信息
- self->top_frame = NULL;
- self->exc_type = NULL;
- self->exc_value = NULL;
- self->exc_traceback = NULL;
- self->recursion_depth = PyThreadState_GET()->recursion_depth;
- /* restore arguments in case they are clobbered */
- ts_target = self;
- ts_passaround_args = args;
- ts_passaround_kwargs = kwargs;
- /* perform the initial switch */
- //栈切换,将当前环境所属的greenlet栈换出去,然后将要执行的greenlet的栈换进来
- //这里为啥会返回两次呢?
- //对于切换到的新的greenlet,因为在执行栈的保存的时候直接提前对于slp_switch函数返回了1 ,接下来就就开始run的执行
- //而对于原来的greenlet,它的运行就已经停在了g_switchstack,并没有进行下去了,以后switch这个greenlet的时候将在这里再返回一次
- //但是这个时候将会返回0
- err = g_switchstack();
- /* returns twice!
- The 1st time with err=1: we are in the new greenlet
- The 2nd time with err=0: back in the caller's greenlet
- */
- //当返回1的时候,表示是在新创建的greenlet里面,如果是0,那么是在调用环境的greenlet里面
- //如果是在新的栈里面,那么需要执行run
- if (err == 1) {
- /* in the new greenlet */
- PyGreenlet* origin;
- #if GREENLET_USE_TRACING
- PyObject* tracefunc;
- #endif
- PyObject* result;
- PyGreenlet* parent;
- self->stack_start = (char*) 1; /* running */ //这里其实主要是充当标识
- /* grab origin while we still can */
- origin = ts_origin; //指向切换之前的greenlet
- ts_origin = NULL;
- /* now use run_info to store the statedict */
- o = self->run_info;
- self->run_info = green_statedict(self->parent);
- Py_INCREF(self->run_info);
- Py_XDECREF(o);
- #if GREENLET_USE_TRACING
- if ((tracefunc = PyDict_GetItem(self->run_info, ts_tracekey)) != NULL) {
- Py_INCREF(tracefunc);
- if (g_calltrace(tracefunc, args ? ts_event_switch : ts_event_throw, origin, self) < 0) {
- /* Turn trace errors into switch throws */
- Py_CLEAR(kwargs);
- Py_CLEAR(args);
- }
- Py_DECREF(tracefunc);
- }
- #endif
- Py_DECREF(origin);
- if (args == NULL) {
- /* pending exception */
- result = NULL;
- } else {
- /* call g.run(*args, **kwargs) */
- //执行run,然后获取返回的参数
- result = PyEval_CallObjectWithKeywords( //开始执行当前协程的run
- run, args, kwargs);
- Py_DECREF(args);
- Py_XDECREF(kwargs);
- }
- Py_DECREF(run);
- result = g_handle_exit(result); //这里对run的返回参数进行一下预处理
- /* jump back to parent */
- //这里实现了跳转到parent的逻辑
- //这里可以看出,对于已经运行完了的greenlet对象,它之后是切换到parent对象上继续执行
- self->stack_start = NULL; /* dead */ //用这个标记当前greenlet已经结束了,待会在switch的时候就不会保存这个栈的数据到内存了
- for (parent = self->parent; parent != NULL; parent = parent->parent) {
- /*
- ** 这里要看到这里将run执行完之后返回的result参数传入了switch
- **
- */
- result = g_switch(parent, result, NULL);
- /* Return here means switch to parent failed,
- * in which case we throw *current* exception
- * to the next parent in chain.
- */
- assert(result == NULL);
- }
- /* We ran out of parents, cannot continue */
- PyErr_WriteUnraisable((PyObject *) self);
- Py_FatalError("greenlets cannot continue");
- }
- /* back in the parent */
- if (err < 0) {
- /* start failed badly, restore greenlet state */
- self->stack_start = NULL;
- self->stack_stop = NULL;
- self->stack_prev = NULL;
- }
- return err;
- }
这里可以看到,确实是将当前gr1的栈底stack_stop指向了刚刚传进来的地址,基本上上面的注释也说的比较的清楚了。。其实真正的栈的复制与切换是在如下代码:
- //这里为啥会返回两次呢?
- //对于切换到的新的greenlet,因为在执行栈的保存的时候直接提前对于slp_switch函数返回了1 ,接下来就就开始run的执行
- //而对于原来的greenlet,它的运行就已经停在了g_switchstack,并没有进行下去了,以后switch这个greenlet的时候将在这里再返回一次
- //但是这个时候将会返回0
- err = g_switchstack();
嗯,这里将会要返回两次额。。也就是会得到两个err的值。。。为啥要返回两次了。。其实理解到了这个也就理解到了整个greenlet切换的关键了。。。返回1是在我们新的greenlet的执行环境中,也就是gr1,如果返回0,那么就是在调用switch方法所属的greenlet的环境中。。。
好了这里也说不明白为啥会返回两次。。接下来看代码吧:
- //将栈转移到真正要执行的地方去
- //这里将会调用使用汇编实现的slp_switch的方法,修改栈寄存器,从而将程序的运行引导到新的greenlet里面去
- static int g_switchstack(void)
- {
- /* Perform a stack switch according to some global variables
- that must be set before:
- - ts_current: current greenlet (holds a reference)
- - ts_target: greenlet to switch to (weak reference) 要转移到的greenlet
- - ts_passaround_args: NULL if PyErr_Occurred(),
- else a tuple of args sent to ts_target (holds a reference)
- - ts_passaround_kwargs: switch kwargs (holds a reference)
- On return results are passed via global variables as well:
- - ts_origin: originating greenlet (holds a reference)
- - ts_current: current greenlet (holds a reference)
- - ts_passaround_args: NULL if PyErr_Occurred(),
- else a tuple of args sent to ts_current (holds a reference)
- - ts_passaround_kwargs: switch kwargs (holds a reference)
- It is very important that stack switch is 'atomic', i.e. no
- calls into other Python code allowed (except very few that
- are safe), because global variables are very fragile.
- */
- int err; //将当前栈的运行信息保存起来,以后运行的时候再恢复
- { /* save state */
- PyGreenlet* current = ts_current; //获取当前在线的greenlet对象
- PyThreadState* tstate = PyThreadState_GET(); //获取当前线程信息
- current->recursion_depth = tstate->recursion_depth; //栈深度
- //可以理解为当前环境所属的greenlet的执行到这里就终止了,以后恢复也会到这里来
- current->top_frame = tstate->frame; //保存当前线程执行栈的顶部帧 ,其实主要是为了处理程序计数器
- current->exc_type = tstate->exc_type; //执行类型
- current->exc_value = tstate->exc_value;
- current->exc_traceback = tstate->exc_traceback;
- }
- //其实如果是新运行的greenlet,这里没有什么可以换进来的
- //这里需要理解的是:假如是A切换到B,那么A的运行将在slp_switch随着栈的切换而停滞,而B的运行将在这个里面开始,当以后重新切换到A的时候,其实也是从slp_switch开始恢复的
- err = slp_switch(); //这里进行栈的切换,到这里就可以理解为当前环境greenlet的栈信息就已经切换出去了 ,然后新的greenlet对象的栈信息被换进来了
- //返回小于0标示出错了
- if (err < 0) { /* error */
- PyGreenlet* current = ts_current;
- current->top_frame = NULL;
- current->exc_type = NULL;
- current->exc_value = NULL;
- current->exc_traceback = NULL;
- assert(ts_origin == NULL);
- ts_target = NULL;
- } else { //转移到ts_target的栈,这里重新设置当前的运行信息
- PyGreenlet* target = ts_target;
- PyGreenlet* origin = ts_current;
- PyThreadState* tstate = PyThreadState_GET();
- tstate->recursion_depth = target->recursion_depth;
- tstate->frame = target->top_frame; //这里主要是为了设置python的程序计数器
- target->top_frame = NULL;
- tstate->exc_type = target->exc_type;
- target->exc_type = NULL;
- tstate->exc_value = target->exc_value;
- target->exc_value = NULL;
- tstate->exc_traceback = target->exc_traceback;
- target->exc_traceback = NULL;
- assert(ts_origin == NULL);
- Py_INCREF(target);
- ts_current = target; //将当前ts_current 设置为switch到的target
- ts_origin = origin; //将ts_origin指向前面
- ts_target = NULL;
- }
- return err;
- }
这里保存了当前环境所属的greenlet的状态信息,然后调用汇编写的slp_switch方法来进行真正的栈切换,接着更改一些全局变量。。。
那么这里的关键就是要理解slp_switch方法了。。
根据不同的系统,会有不同的实现。。因为我的时mac os 64位,所以实现如下:
- //因为当前所属的greenlet的执行栈肯定是在栈层次最上面的
- //如果这里要切换到的greenlet的栈其实还在下面,那么需要将前面的栈的信息都拷贝到堆空间里面去
- //当然这里也存在特殊情况,例如切换到的时新的greenlet,那么其实就不用拷贝
- static int
- slp_switch(void)
- {
- int err;
- void* rbp;
- void* rbx;
- unsigned int csr;
- unsigned short cw;
- register long *stackref, stsizediff;
- __asm__ volatile ("" : : : REGS_TO_SAVE);
- __asm__ volatile ("fstcw %0" : "=m" (cw));
- __asm__ volatile ("stmxcsr %0" : "=m" (csr));
- //这里先获取以及保存rbp,rbx,rsp三个寄存器的数据
- __asm__ volatile ("movq %%rbp, %0" : "=m" (rbp));
- __asm__ volatile ("movq %%rbx, %0" : "=m" (rbx));
- __asm__ ("movq %%rsp, %0" : "=g" (stackref)); //将栈顶的地址(rsp)保存到stackref
- {
- //这里会将栈之间的偏移保存到stsizediff变量
- //这里是宏定义,如果要切换到的greenlet并没有运行,那么这里宏定义里面就会提前返回1那么g_initialstub里面就可以开始run的执行了
- //这里可以看到其实栈的保存是最终地址保存到slp_switch这一层的
- SLP_SAVE_STATE(stackref, stsizediff); // 把栈的信息保存到堆空间里面去
- //修改rbp以及rsp寄存器值,用于将当前栈切换到新要执行的greenlet的执行栈
- __asm__ volatile (
- "addq %0, %%rsp\n"
- "addq %0, %%rbp\n"
- :
- : "r" (stsizediff)
- );
- SLP_RESTORE_STATE(); //对于刚刚运行的greenlet,这里其实没有什么恢复的
- __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err));
- }
- // 这里要注意,前后rbp,rbx的值已经改变了,因为栈的数据已经改变了
- //因为这里已经恢复了切换到的greenlet的栈的信息,所以这里恢复的其实是切换到的greenlet的栈的寄存器的地址
- __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx));
- __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp));
- __asm__ volatile ("ldmxcsr %0" : : "m" (csr));
- __asm__ volatile ("fldcw %0" : : "m" (cw));
- __asm__ volatile ("" : : : REGS_TO_SAVE);
- return err;
- }
这里最主要的就是先保存了rbp以及rbx寄存器的值,然后将rsp寄存器的值保存到寄存器变量stackref中,
rbp:当前函数的栈底指针
rsp:当前函数栈的栈顶指针
然后调用SLP_SAVE_STATE 来进行当前栈的保存,这个是一个宏定义,如下:
- //第一个参数是当前栈的顶部的地址,第二个变量用于保存栈之间的偏移
- //这里如果要切换到的greenlet对象还没有开始,那么返回1
- #define SLP_SAVE_STATE(stackref, stsizediff) \
- stackref += STACK_MAGIC; \
- if (slp_save_state((char*)stackref)) return -1; \
- if (!PyGreenlet_ACTIVE(ts_target)) return 1; \
- stsizediff = ts_target->stack_start - (char*)stackref
这里先是调用slp_save_state方法来保存栈,如下:
- //保存栈的信息,参数是当前栈顶的地址
- //这里保存的原则就是,将要切换到的greenlet的对象的栈更上层的栈都保存起来
- static int GREENLET_NOINLINE(slp_save_state)(char* stackref)
- {
- /* must free all the C stack up to target_stop */
- char* target_stop = ts_target->stack_stop; //获取要切换到的greenlet的栈底部
- PyGreenlet* owner = ts_current; //当前环境所属的greenlet
- assert(owner->stack_saved == 0);
- if (owner->stack_start == NULL) { //这个表示当前greenlet已经执行完了,那么指向栈层次的上一个greenlet
- owner = owner->stack_prev; /* not saved if dying */
- } else {
- owner->stack_start = stackref; //设置当前环境所属greenlet的栈的顶部地址
- }
- //这里是要保存所有在当前栈层次上更深的栈的信息,将他们保存起来防止干扰
- while (owner->stack_stop < target_stop) { //因为栈是从高地址向低地址部分延伸的
- /* ts_current is entierely within the area to free */
- if (g_save(owner, owner->stack_stop)) //将这个greenlet的栈的信息保存到堆空间
- return -1; /* XXX */
- owner = owner->stack_prev; //向上遍历
- }
- //这里确实存在可能等于的情况,例如在mai中创建一个协程T1,那么它的parent也就是root,然后switch进行调度,当执行完了之后,start_start会被设置为null
- //接下来会被switch到parent,那么在上面,owner将会北被指向prev,其实也就是parent,这里就相等了
- if (owner != ts_target) { //如果当前环境的greelet不是要切换到的greenlet,那么保存当前
- if (g_save(owner, target_stop))
- return -1; /* XXX */
- }
- return 0;
- }
这里可以代码的大体意思就是将属于要切换到的greenlet gr1的栈 更上面greenlet的栈数据都保存起来。。
而这里,ts_current全局变量其实是最开始初始化建立的root greenlet对象,它的栈的栈底指针值是最大的,所以最终只会执行如下代码:
- if (owner != ts_target) { //如果当前环境的greelet不是要切换到的greenlet,那么保存当前
- if (g_save(owner, target_stop))
- return -1; /* XXX */
- }
而且,这里传进来的stackref参数其实是slp_switch方法的栈顶地址,我们来看看当前栈的情况:
再看看g_save方法:
- //将这个greenlet的栈的信息保存到堆空间里面去
- static int g_save(PyGreenlet* g, char* stop)
- {
- /* Save more of g's stack into the heap -- at least up to 'stop'
- g->stack_stop |________|
- | |
- | __ stop . . . . .
- | | ==> . .
- |________| _______
- | | | |
- | | | |
- g->stack_start | | |_______| g->stack_copy
- */
- intptr_t sz1 = g->stack_saved;
- //计算要保存的栈的大小
- intptr_t sz2 = stop - g->stack_start; //栈的大小
- assert(g->stack_start != NULL);
- if (sz2 > sz1) { //标示栈的大小已经改变了
- char* c = (char*)PyMem_Realloc(g->stack_copy, sz2);
- if (!c) {
- PyErr_NoMemory();
- return -1;
- }
- memcpy(c+sz1, g->stack_start+sz1, sz2-sz1);
- g->stack_copy = c; //表示栈的信息保存到了这个地方
- g->stack_saved = sz2; //保存了这么大的大小
- }
- return 0;
- }
可以看到,其实保存的是dummymarker到slp_switch栈顶地址这段空间的值。。嗯,这个很重要。。。
接着因为我们切换到的greenlet gr1 并没有运行,所以直接提前返回1了,那么当前slp_switch将会返回1,然后g_switchstack方法也就返回了1,那么也就是解释了在g_initialstub方法中的第一个返回1是怎么来的。。。
那么当前的执行回到了g_initialstub方法中,因为返回了1,所以可以执行如下代码了:
- result = PyEval_CallObjectWithKeywords( //开始执行当前协程的run
- run, args, kwargs);
这里就是调用新的greenlet 也就是gr1 的run也就是我们python代码中的test1函数。。。
- def test1(name):
- print name
- gr2.switch()
- print "over"
用上述同理的方法,来分析gr2.switch(),这里将会切换到gr2的执行,也就是test2函数,在执行之前gr2的栈的内容也将会被保存。。。
- def test2():
- print "test-2"
- return "test2-fjsfjs"
当test2执行完了之后,将会执行回到 g_initialstub中,执行如下代码:
- /* jump back to parent */
- //这里实现了跳转到parent的逻辑
- //这里可以看出,对于已经运行完了的greenlet对象,它之后是切换到parent对象上继续执行
- self->stack_start = NULL; /* dead */ //用这个标记当前greenlet已经结束了,待会在switch的时候就不会保存这个栈的数据到内存了
- for (parent = self->parent; parent != NULL; parent = parent->parent) {
- /*
- ** 这里要看到这里将run执行完之后返回的result参数传入了switch
- **
- */
- result = g_switch(parent, result, NULL);
- /* Return here means switch to parent failed,
- * in which case we throw *current* exception
- * to the next parent in chain.
- */
- assert(result == NULL);
- }
也就是将切换到parent来运行,这里parent也就是最开始初始化建立的greenlet对象。。。
那么这里在slp_switch方法中就会产生不一样的地方了。。。
- //因为当前所属的greenlet的执行栈肯定是在栈层次最上面的
- //如果这里要切换到的greenlet的栈其实还在下面,那么需要将前面的栈的信息都拷贝到堆空间里面去
- //当然这里也存在特殊情况,例如切换到的时新的greenlet,那么其实就不用拷贝
- static int
- slp_switch(void)
- {
- int err;
- void* rbp;
- void* rbx;
- unsigned int csr;
- unsigned short cw;
- register long *stackref, stsizediff;
- __asm__ volatile ("" : : : REGS_TO_SAVE);
- __asm__ volatile ("fstcw %0" : "=m" (cw));
- __asm__ volatile ("stmxcsr %0" : "=m" (csr));
- //这里先获取以及保存rbp,rbx,rsp三个寄存器的数据
- __asm__ volatile ("movq %%rbp, %0" : "=m" (rbp));
- __asm__ volatile ("movq %%rbx, %0" : "=m" (rbx));
- __asm__ ("movq %%rsp, %0" : "=g" (stackref)); //将栈顶的地址(rsp)保存到stackref
- {
- //这里会将栈之间的偏移保存到stsizediff变量
- //这里是宏定义,如果要切换到的greenlet并没有运行,那么这里宏定义里面就会提前返回1那么g_initialstub里面就可以开始run的执行了
- //这里可以看到其实栈的保存是最终地址保存到slp_switch这一层的
- SLP_SAVE_STATE(stackref, stsizediff); // 把栈的信息保存到堆空间里面去
- //修改rbp以及rsp寄存器值,用于将当前栈切换到新要执行的greenlet的执行栈
- __asm__ volatile (
- "addq %0, %%rsp\n"
- "addq %0, %%rbp\n"
- :
- : "r" (stsizediff)
- );
- SLP_RESTORE_STATE(); //对于刚刚运行的greenlet,这里其实没有什么恢复的
- __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err));
- }
- // 这里要注意,前后rbp,rbx的值已经改变了,因为栈的数据已经改变了,虽然代码指令没有变,但是栈已经变了
- //因为这里已经恢复了切换到的greenlet的栈的信息,所以这里恢复的其实是切换到的greenlet的栈的寄存器的地址
- __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx));
- __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp));
- __asm__ volatile ("ldmxcsr %0" : : "m" (csr));
- __asm__ volatile ("fldcw %0" : : "m" (cw));
- __asm__ volatile ("" : : : REGS_TO_SAVE);
- return err;
- }
这里在执行宏定义SLP_SAVE_STATE的时候将不会提前将slp_switch函数返回,而是将会计算得到当前gr2的栈与parent栈的偏移量,存在stsizediff寄存器变量中。。。
然后通过汇编来修改rsp与rbp寄存器的值,达到切换栈的效果。。。
然后SLP_RESTORE_STATE调用这个宏定义将以前保存的parent 的栈数据。。。前面已经说过了,也就是:
也就是说,接下来执行的代码
- __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err));
- }
- // 这里要注意,前后rbp,rbx的值已经改变了,因为栈的数据已经改变了,虽然代码指令没有变,但是栈已经变了
- //因为这里已经恢复了切换到的greenlet的栈的信息,所以这里恢复的其实是切换到的greenlet的栈的寄存器的地址
- __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx));
- __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp));
- __asm__ volatile ("ldmxcsr %0" : : "m" (csr));
- __asm__ volatile ("fldcw %0" : : "m" (cw));
- __asm__ volatile ("" : : : REGS_TO_SAVE);
- return err;
他们的运行已经在以前parent,也就是初始化建立的greenlet对象的栈里面执行了。。。这里也就解释了为啥在g_initialstub函数中,g_switchstack会返回两次,前面一次会返回1,而这一次会返回0.。。
那么到这里value = gr1.switch("fjsfjs")也就执行完了。。
回到最开的python代码:
执行:print value
到这里greenlet是如何实现栈的复制与切换就说的差不多了。。。。
当然还有一些东西没有说,例如参数的传递啥的。。就不细说了,看代码都能比较容易明白。。。