【Python】运行环境初始化

0x01 线程环境初始化

线程模型回顾

Python启动后,真正有意义的初始化动作是从Py_Initialize开始(当然Py_Initialize之前也做了很多复杂的动作),Py_Initialize中调用了Py_InitializeEx

// python.c -- Minimal main program -- everything is loaded from the library
int
main(int argc, char **argv)
{
    /* 754 requires that FP exceptions run in "no stop" mode by default,
     * and until C vendors implement C99's ways to control FP exceptions,
     * Python requires non-stop mode.  Alas, some platforms enable FP
     * exceptions by default.  Here we disable them.
     */
#ifdef __FreeBSD__
    fp_except_t m;

    m = fpgetmask();
    fpsetmask(m & ~FP_X_OFL);
#endif
    return Py_Main(argc, argv);
}

// main.c -- Python interpreter main program
/* Main program */
int
Py_Main(int argc, char **argv)
{
    ......
    Py_Initialize
    ......
}

//Pythonrun.c
void
Py_Initialize(void)
{
    Py_InitializeEx(1);
}

void
Py_InitializeEx(int install_sigs)
{
    PyInterpreterState *interp;
    PyThreadState *tstate;
    PyObject *bimod, *sysmod;
    char *p;

    extern void _Py_ReadyTypes(void);

    if (initialized)
        return;
    initialized = 1;

    if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0')
        Py_DebugFlag = add_flag(Py_DebugFlag, p);
    if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0')
        Py_VerboseFlag = add_flag(Py_VerboseFlag, p);
    if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0')
        Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p);
    // 创建进程状态
    interp = PyInterpreterState_New();
    if (interp == NULL)
        Py_FatalError("Py_Initialize: can't make first interpreter");
    // 创建线程状态
    tstate = PyThreadState_New(interp);
    if (tstate == NULL)
        Py_FatalError("Py_Initialize: can't make first thread");
    (void) PyThreadState_Swap(tstate);
    // 初始化类型模型
    _Py_ReadyTypes();

    if (!_PyFrame_Init())
        Py_FatalError("Py_Initialize: can't init frames");

    if (!_PyInt_Init())
        Py_FatalError("Py_Initialize: can't init ints");

    _PyFloat_Init();
    // 初始化modules
    interp->modules = PyDict_New();
    if (interp->modules == NULL)
        Py_FatalError("Py_Initialize: can't make modules dictionary");
    interp->modules_reloading = PyDict_New();
    if (interp->modules_reloading == NULL)
        Py_FatalError("Py_Initialize: can't make modules_reloading dictionary");
    // 初始化__built__ module
    bimod = _PyBuiltin_Init();
    if (bimod == NULL)
        Py_FatalError("Py_Initialize: can't initialize __builtin__");
    interp->builtins = PyModule_GetDict(bimod);
    if (interp->builtins == NULL)
        Py_FatalError("Py_Initialize: can't initialize builtins dict");
    Py_INCREF(interp->builtins);
    // 初始化sys module
    sysmod = _PySys_Init();
    if (sysmod == NULL)
        Py_FatalError("Py_Initialize: can't initialize sys");
    interp->sysdict = PyModule_GetDict(sysmod);
    if (interp->sysdict == NULL)
        Py_FatalError("Py_Initialize: can't initialize sys dict");
    Py_INCREF(interp->sysdict);
    _PyImport_FixupExtension("sys", "sys");
    // 设置module搜索路径集合
    PySys_SetPath(Py_GetPath());
    // 设置了sys.modules,他就是interp->modules
    PyDict_SetItemString(interp->sysdict, "modules",
                 interp->modules);
    // 初始化import机制的环境
    _PyImport_Init();

    /* initialize builtin exceptions */
    // 初始化Python内建的exceptions
    _PyExc_Init();
    // 备份exceptions module 和 __builtin__ module
    _PyImport_FixupExtension("exceptions", "exceptions");
    /* phase 2 of builtins */
    _PyImport_FixupExtension("__builtin__", "__builtin__");
    // 在sys module中添加一些对象,用于import机制
    _PyImportHooks_Init();

    if (install_sigs)
        initsigs(); /* Signal handling stuff, including initintr() */

    initmain(); /* Module __main__ */
    if (!Py_NoSiteFlag)
        initsite(); /* Module site */

    warnings_module = PyImport_ImportModule("warnings");
    if (!warnings_module)
        PyErr_Clear();
}

Py_InitializeEx中完成了一个重要工作就是加载多个基础的module__builtin__sys等),还会完成Python类型系统的初始化和异常系统初始化。

// pystate.h
typedef struct _is {

    struct _is *next;
    struct _ts *tstate_head;    // 模拟进程环境中的线程集合
    PyObject *modules;
    PyObject *sysdict;
    PyObject *builtins;
    PyObject *modules_reloading;

    PyObject *codec_search_path;
    PyObject *codec_search_cache;
    PyObject *codec_error_registry;
} PyInterpreterState;

typedef struct _ts {
    struct _ts *next;
    PyInterpreterState *interp;

    struct _frame *frame;    // 模拟线程中的函数调用堆栈
    int recursion_depth;
    int tracing;
    int use_tracing;

    Py_tracefunc c_profilefunc;
    Py_tracefunc c_tracefunc;
    PyObject *c_profileobj;
    PyObject *c_traceobj;

    PyObject *curexc_type;
    PyObject *curexc_value;
    PyObject *curexc_traceback;

    PyObject *exc_type;
    PyObject *exc_value;
    PyObject *exc_traceback;

    PyObject *dict;  /* Stores per-thread state */

    int tick_counter;

    int gilstate_counter;

    PyObject *async_exc; /* Asynchronous exception to raise */
    long thread_id; /* Thread id where this tstate was created */

    int trash_delete_nesting;
    PyObject *trash_delete_later;
} PyThreadState;
某时刻Python运行时的整个环境

初始化线程环境

win32环境,当执行一个可执行文件时,操作系统首先会创建一个进程内核对象。同样,在Python中也是如此,在Py_InitializeEx的开始处,Python会首先调用PyInterpreterState_New创建一个崭新的PyInterpreterState对象。

// pystate.c
static PyInterpreterState *interp_head = NULL;

PyInterpreterState *
PyInterpreterState_New(void)
{
    PyInterpreterState *interp = (PyInterpreterState *)
                     malloc(sizeof(PyInterpreterState));

    if (interp != NULL) {
        HEAD_INIT();
#ifdef WITH_THREAD
        if (head_mutex == NULL)
            Py_FatalError("Can't initialize threads for interpreter");
#endif
        interp->modules = NULL;
        interp->modules_reloading = NULL;
        interp->sysdict = NULL;
        interp->builtins = NULL;
        interp->tstate_head = NULL;
        interp->codec_search_path = NULL;
        interp->codec_search_cache = NULL;
        interp->codec_error_registry = NULL;

        HEAD_LOCK();
        interp->next = interp_head;
        interp_head = interp;
        HEAD_UNLOCK();
    }
    return interp;
}

Python的运行时环境中,有一个全局变量interp_head(一个PyInterpreterState链表)来管理PyInterpreterState对象,在Python运行时可能会有多个PyInterpreterState对象链接在一起(这个就是对操作系统多进程的模拟)。新创建的PyInterpreterState对象如下所示:

新创建的PyInterpreterState对象

在创建了PyInterpreterState进程状态)对象之后,Python会调用PyThreadState_New函数来创建PyThreadState线程状态)对象。

// pystate.c
PyThreadState *
PyThreadState_New(PyInterpreterState *interp)
{
    PyThreadState *tstate = (PyThreadState *)malloc(sizeof(PyThreadState));
    // 设置获得线程中函数调用栈的操作
    if (_PyThreadState_GetFrame == NULL)
        _PyThreadState_GetFrame = threadstate_getframe;

    if (tstate != NULL) {
        // 在PyThreadState对象中关联PyInterpreterState对象
        tstate->interp = interp;

        tstate->frame = NULL;
        tstate->recursion_depth = 0;
        tstate->tracing = 0;
        tstate->use_tracing = 0;
        tstate->tick_counter = 0;
        tstate->gilstate_counter = 0;
        tstate->async_exc = NULL;

        tstate->dict = NULL;

        tstate->curexc_type = NULL;
        tstate->curexc_value = NULL;
        tstate->curexc_traceback = NULL;

        tstate->exc_type = NULL;
        tstate->exc_value = NULL;
        tstate->exc_traceback = NULL;

        tstate->c_profilefunc = NULL;
        tstate->c_tracefunc = NULL;
        tstate->c_profileobj = NULL;
        tstate->c_traceobj = NULL;

        HEAD_LOCK();
        tstate->next = interp->tstate_head;
        // 在PyInterpreterState对象中关联PyThreadState对象
        interp->tstate_head = tstate;
        HEAD_UNLOCK();
    }

    return tstate;
}

PyThreadState结构体中也存在一个next指针,PyThreadStatePython运行时也会创建一个链表(模拟多线程)。

还会设置从线程中获得函数调用栈PyFrameObject对象链表)的方法(threadstate_getframe)。

然后建立PyThreadState对象和PyInterpreterState对象的关系,结果如下所示:

PyThreadState对象和PyInterpreterState对象的关系图示

Python的运行时环境中,有一个全局变量_PyThreadState_Current_PyThreadState_Current维护了当前活动的线程,更准确的说是当前活动线程对应的PyThreadState对象),初始化时该变量为NULL。创建完PyThreadState对象后,会通过PyThreadState_Swap函数设置_PyThreadState_Current的值。

// pystate.c
PyThreadState * PyThreadState_Swap(PyThreadState *newts)
{
    PyThreadState *oldts = _PyThreadState_Current;
    _PyThreadState_Current = newts;
    return oldts;
}

接下来会通过_Py_ReadyTypes函数初始化Python类型系统(参考类机制剖析)。

然后通过_PyFrame_Init函数设置全局变量builtin_object

// pystate.c
static PyObject *builtin_object;

int _PyFrame_Init()
{
    builtin_object = PyString_InternFromString("__builtins__");
    return (builtin_object != NULL);
}

这个内容为__builtins__PyStringObject对象builtin_objectPyFrame_New创建一个新的PyFrameObject对象时会发挥作用。

接下来会初始化一些边边角角的东西。

至此,Py_InitializeEx有了一个阶段性结果,创建了代表进程和线程的PyInterpreterStatePyThreadState对象,并且在他们之间建立了联系。接下来会进入相对独立的环节:设置系统module

0x02 系统module初始化

创建__builtin__ module

Py_INitializeEx中,当Python创建了PyThreadStatePyInterpreterState对象之后,就会开始通过_PyBuiltin_Init来设置系统的__builtin__ module了。在调用_PyBuiltin_Init之前,Python会通过interp->modules创建为一个PyDictObject对象,这个对象中维护着所有的PyThreadState对象共享的资源。

// Bltinmodule.c
PyObject *
_PyBuiltin_Init(void)
{
    PyObject *mod, *dict, *debug;
    // 创建并设置__builtin__ module
    mod = Py_InitModule4("__builtin__", builtin_methods,
                 builtin_doc, (PyObject *)NULL,
                 PYTHON_API_VERSION);
    if (mod == NULL)
        return NULL;
    // 将所有内建类型加到__builtin__ module中
    dict = PyModule_GetDict(mod);
        #ifdef Py_TRACE_REFS
    /* __builtin__ exposes a number of statically allocated objects
     * that, before this code was added in 2.3, never showed up in
     * the list of "all objects" maintained by Py_TRACE_REFS.  As a
     * result, programs leaking references to None and False (etc)
     * couldn't be diagnosed by examining sys.getobjects(0).
     */
#define ADD_TO_ALL(OBJECT) _Py_AddToAllObjects((PyObject *)(OBJECT), 0)
#else
#define ADD_TO_ALL(OBJECT) (void)0
#endif

#define SETBUILTIN(NAME, OBJECT) \
    if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0)   \
        return NULL;                        \
    ADD_TO_ALL(OBJECT)

    SETBUILTIN("None",      Py_None);
    SETBUILTIN("Ellipsis",      Py_Ellipsis);
    SETBUILTIN("NotImplemented",    Py_NotImplemented);
    SETBUILTIN("False",     Py_False);
    SETBUILTIN("True",      Py_True);
    SETBUILTIN("basestring",    &PyBaseString_Type);
    SETBUILTIN("bool",      &PyBool_Type);
    SETBUILTIN("buffer",        &PyBuffer_Type);
    SETBUILTIN("classmethod",   &PyClassMethod_Type);
#ifndef WITHOUT_COMPLEX
    SETBUILTIN("complex",       &PyComplex_Type);
#endif
    SETBUILTIN("dict",      &PyDict_Type);
    SETBUILTIN("enumerate",     &PyEnum_Type);
    SETBUILTIN("file",      &PyFile_Type);
    SETBUILTIN("float",     &PyFloat_Type);
    SETBUILTIN("frozenset",     &PyFrozenSet_Type);
    SETBUILTIN("property",      &PyProperty_Type);
    SETBUILTIN("int",       &PyInt_Type);
    SETBUILTIN("list",      &PyList_Type);
    SETBUILTIN("long",      &PyLong_Type);
    SETBUILTIN("object",        &PyBaseObject_Type);
    SETBUILTIN("reversed",      &PyReversed_Type);
    SETBUILTIN("set",       &PySet_Type);
    SETBUILTIN("slice",     &PySlice_Type);
    SETBUILTIN("staticmethod",  &PyStaticMethod_Type);
    SETBUILTIN("str",       &PyString_Type);
    SETBUILTIN("super",     &PySuper_Type);
    SETBUILTIN("tuple",     &PyTuple_Type);
    SETBUILTIN("type",      &PyType_Type);
    SETBUILTIN("xrange",        &PyRange_Type);
#ifdef Py_USING_UNICODE
    SETBUILTIN("unicode",       &PyUnicode_Type);
#endif
    debug = PyBool_FromLong(Py_OptimizeFlag == 0);
    if (PyDict_SetItemString(dict, "__debug__", debug) < 0) {
        Py_XDECREF(debug);
        return NULL;
    }
    Py_XDECREF(debug);

    return mod;
#undef ADD_TO_ALL
#undef SETBUILTIN
}

整个函数的功能就是设置好__builtin__ module。总共分为两个步骤:

  • 创建PyModuleObject对象,在Python中,module正是通过和这个对象来设置的
  • 设置module,将Python中的所有的类型对象全塞进新创建的__builtin__ module中。

在第一步中已经设置好了大部分的内容,由函数Py_InitModule4实现:

// modsupport.c
PyObject *
Py_InitModule4(const char *name, PyMethodDef *methods, const char *doc,
           PyObject *passthrough, int module_api_version)
{
    PyObject *m, *d, *v, *n;
    PyMethodDef *ml;
    ......
    // 创建module对象
    if ((m = PyImport_AddModule(name)) == NULL)
        return NULL;
    // 设置module中的(符号,值)对应关系
    d = PyModule_GetDict(m);
    if (methods != NULL) {
        n = PyString_FromString(name);
        if (n == NULL)
            return NULL;
        // 遍历methods指定的module对象中应该包含的操作集合
        for (ml = methods; ml->ml_name != NULL; ml++) {
            if ((ml->ml_flags & METH_CLASS) ||
                (ml->ml_flags & METH_STATIC)) {
                PyErr_SetString(PyExc_ValueError,
                        "module functions cannot set"
                        " METH_CLASS or METH_STATIC");
                Py_DECREF(n);
                return NULL;
            }
            v = PyCFunction_NewEx(ml, passthrough, n);
            if (v == NULL) {
                Py_DECREF(n);
                return NULL;
            }
            if (PyDict_SetItemString(d, ml->ml_name, v) != 0) {
                Py_DECREF(v);
                Py_DECREF(n);
                return NULL;
            }
            Py_DECREF(v);
        }
        Py_DECREF(n);
    }
    if (doc != NULL) {
        v = PyString_FromString(doc);
        if (v == NULL || PyDict_SetItemString(d, "__doc__", v) != 0) {
            Py_XDECREF(v);
            return NULL;
        }
        Py_DECREF(v);
    }
    return m;
}

Py_InitModule4参数的含义:

  • namemodule的名称,在这里是__builtin__
  • methods:该module中所包含的函数的集合,在这里是builtin_methods
  • docmodule的文档,在这里是builtin_doc
  • passthrough:这个参数在Python 2.5中没有使用,为NULL
  • module_api_versionPython内部使用的version值,用于比较

Py_InitModule4函数可以分为2个独立的部分:创建module对象;将(符号,值)对应关系放置到module中。

创建module对象

// import.c
PyObject *
PyImport_AddModule(const char *name)
{
    // 获得Python维护的module集合
    PyObject *modules = PyImport_GetModuleDict();
    PyObject *m;
    // 如果module集合中没有名为name的module对象,就创建它,否则直接返回
    if ((m = PyDict_GetItemString(modules, name)) != NULL &&
        PyModule_Check(m))
        return m;
    m = PyModule_New(name);
    if (m == NULL)
        return NULL;
    // 将创建的module对象放入Python的全局module集合中
    if (PyDict_SetItemString(modules, name, m) != 0) {
        Py_DECREF(m);
        return NULL;
    }
    Py_DECREF(m); /* Yes, it still exists, in modules! */
    return m;
}

Python中维护了一个存放所有加载到内存中的module集合,在这个集合中,存放着所有的(module名,module对象)这样的对应关系,对应到Python中就是sys.modules。在创建一个新的module对象之前,会先到这个全局module中查看是否已经存在。通过PyImport_GetModuleDict函数获得PyInterpreterState(进程状态)对象中的module域的值。

如果全局modules字典中没有存在该namemodule对象,则通过PyModule_New函数创建一个新的module对象,然后将(name,module)对应关系插入到modules集合中。

// moduleobject.c
typedef struct {
    PyObject_HEAD
    PyObject *md_dict;
} PyModuleObject;

PyObject * PyModule_New(const char *name)
{
    PyModuleObject *m;
    PyObject *nameobj;
    m = PyObject_GC_New(PyModuleObject, &PyModule_Type);
    if (m == NULL)
        return NULL;
    nameobj = PyString_FromString(name);
    m->md_dict = PyDict_New();
    if (m->md_dict == NULL || nameobj == NULL)
        goto fail;
    if (PyDict_SetItemString(m->md_dict, "__name__", nameobj) != 0)
        goto fail;
    if (PyDict_SetItemString(m->md_dict, "__doc__", Py_None) != 0)
        goto fail;
    Py_DECREF(nameobj);
    PyObject_GC_Track(m);
    return (PyObject *)m;

 fail:
    Py_XDECREF(nameobj);
    Py_DECREF(m);
    return NULL;
}

实际上,PyModuleObject对象就是对PyDictObject对象的简单包装,创建PyModuleObject对象的动作很简单,注意在这里设置了module__name__属性,但是没有设置其__doc__属性。

至此,创建的PyModuleObject对象还算是空的,接下来就开始设置module对象。

设置module对象

设置module对象的流程回到Py_InitModule4函数中,设置属性主要依赖Py_InitModule4函数的第二个参数(methods),在这里为builtin_methods,遍历builtin_methods,处理其中的每一个元素。

// methodobject.h
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
struct PyMethodDef {
    const char  *ml_name;   /* The name of the built-in function/method */
    PyCFunction  ml_meth;   /* The C function that implements it */
    int      ml_flags;  /* Combination of METH_xxx flags, which mostly
                   describe the args expected by the C func */
    const char  *ml_doc;    /* The __doc__ attribute, or NULL */
};
typedef struct PyMethodDef PyMethodDef;

// bltinmodule.c
static PyMethodDef builtin_methods[] = {
    {"__import__",  (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc},
    {"abs",     builtin_abs,        METH_O, abs_doc},
    {"all",     builtin_all,        METH_O, all_doc},
    {"any",     builtin_any,        METH_O, any_doc},
    {"apply",   builtin_apply,      METH_VARARGS, apply_doc},
    {"callable",    builtin_callable,   METH_O, callable_doc},
    {"chr",     builtin_chr,        METH_VARARGS, chr_doc},
    {"cmp",     builtin_cmp,        METH_VARARGS, cmp_doc},
    {"coerce",  builtin_coerce,     METH_VARARGS, coerce_doc},
    {"compile", builtin_compile,    METH_VARARGS, compile_doc},
    {"delattr", builtin_delattr,    METH_VARARGS, delattr_doc},
    {"dir",     builtin_dir,        METH_VARARGS, dir_doc},
    {"divmod",  builtin_divmod,     METH_VARARGS, divmod_doc},
    {"eval",    builtin_eval,       METH_VARARGS, eval_doc},
    {"execfile",    builtin_execfile,   METH_VARARGS, execfile_doc},
    {"filter",  builtin_filter,     METH_VARARGS, filter_doc},
    {"getattr", builtin_getattr,    METH_VARARGS, getattr_doc},
    {"globals", (PyCFunction)builtin_globals,    METH_NOARGS, globals_doc},
    {"hasattr", builtin_hasattr,    METH_VARARGS, hasattr_doc},
    {"hash",    builtin_hash,       METH_O, hash_doc},
    {"hex",     builtin_hex,        METH_O, hex_doc},
    {"id",      builtin_id,         METH_O, id_doc},
    {"input",   builtin_input,      METH_VARARGS, input_doc},
    {"intern",  builtin_intern,     METH_VARARGS, intern_doc},
    {"isinstance",  builtin_isinstance, METH_VARARGS, isinstance_doc},
    {"issubclass",  builtin_issubclass, METH_VARARGS, issubclass_doc},
    {"iter",    builtin_iter,       METH_VARARGS, iter_doc},
    {"len",     builtin_len,        METH_O, len_doc},
    {"locals",  (PyCFunction)builtin_locals,     METH_NOARGS, locals_doc},
    {"map",     builtin_map,        METH_VARARGS, map_doc},
    {"max",     (PyCFunction)builtin_max,        METH_VARARGS | METH_KEYWORDS, max_doc},
    {"min",     (PyCFunction)builtin_min,        METH_VARARGS | METH_KEYWORDS, min_doc},
    {"oct",     builtin_oct,        METH_O, oct_doc},
    {"open",    (PyCFunction)builtin_open,       METH_VARARGS | METH_KEYWORDS, open_doc},
    {"ord",     builtin_ord,        METH_O, ord_doc},
    {"pow",     builtin_pow,        METH_VARARGS, pow_doc},
    {"range",   builtin_range,      METH_VARARGS, range_doc},
    {"raw_input",   builtin_raw_input,  METH_VARARGS, raw_input_doc},
    {"reduce",  builtin_reduce,     METH_VARARGS, reduce_doc},
    {"reload",  builtin_reload,     METH_O, reload_doc},
    {"repr",    builtin_repr,       METH_O, repr_doc},
    {"round",   (PyCFunction)builtin_round,      METH_VARARGS | METH_KEYWORDS, round_doc},
    {"setattr", builtin_setattr,    METH_VARARGS, setattr_doc},
    {"sorted",  (PyCFunction)builtin_sorted,     METH_VARARGS | METH_KEYWORDS, sorted_doc},
    {"sum",     builtin_sum,        METH_VARARGS, sum_doc},
#ifdef Py_USING_UNICODE
    {"unichr",  builtin_unichr,     METH_VARARGS, unichr_doc},
#endif
    {"vars",    builtin_vars,       METH_VARARGS, vars_doc},
    {"zip",         builtin_zip,        METH_VARARGS, zip_doc},
    {NULL,      NULL},
};
len函数对应的PyMethodDef结构

对于builtin_methods中的每一个PyMethodDef结构,PyInitModule4都会基于它创建一个PyCFunctionObject对象(Python中对函数指针的包装)。

// methodobject.h
typedef struct {
    PyObject_HEAD
    PyMethodDef *m_ml; /* Description of the C function to call */
    PyObject    *m_self; /* Passed as 'self' arg to the C func, can be NULL */
    PyObject    *m_module; /* The __module__ attribute, can be anything */
} PyCFunctionObject;

// methodobject.c
PyObject *
PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
{
    PyCFunctionObject *op;
    op = free_list;
    if (op != NULL) {
        free_list = (PyCFunctionObject *)(op->m_self);
        PyObject_INIT(op, &PyCFunction_Type);
    }
    else {
        op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);
        if (op == NULL)
            return NULL;
    }
    op->m_ml = ml;
    Py_XINCREF(self);
    op->m_self = self;
    Py_XINCREF(module);
    op->m_module = module;
    _PyObject_GC_TRACK(op);
    return (PyObject *)op;
}

PyCFunctionObject对象中的那个self,也就是在Py_InitModule4中传入的passthrough(之前说这个参数在Python 2.5中没用,所以这里的self也就为NULL)。

注意,PyCFunctionObject对象中的m_module域并不是指向一个真正的PyModuleObject对象,而是一个PyStringObject对象,但是这个PyStringObject对象正是PyModuleObject对象的名字(根据PyModuleObject对象的名字可以在全局modules中找到对应的PyModuleObject对象)。

创建完成的 __builtin__ module

__builtin__ module创建完以后,也就是bimod = _PyBuiltin_Init()执行完以后,将PyModuleObject对象中维护的那个PyDictObject对象赋值给interp->builtins。以后Python在需要访问__builtin__ module时,直接访问interp->builtins就行,不需要再到interp->modules中去找到__builtin__ module对象然后再去访问。因为Python中使用__builtin__ module非常频繁,这样的机制会速度会更快。

创建sys module

sys module的备份

Python在创建并设置了__builtin__ module之后,会类似的以同样流程设置sys module,并像设置interp->builtins一样设置interp->sysdict

设置完成__builtin__sys两个module之后,PyInterpreterStatePyThreadState对象在内存中的情形如下所示:

此时内存中情形

由于Pythonmodule集合interp->modules是一个PyDictObject对象,而PyDictObject对象在Python中是一个可变对象(其中维护的(module name,PyModuleObject)元素对在运行时有可能被删除)。

对于Python的扩展modulesys等),为了避免被删除后的重新初始化,Python会将所有的扩展module通过一个全局PyDictObject对象来进行备份维护,这里通过_PyImport_FixupExtension("sys", "sys")函数完成:

// import.c
static PyObject *extensions = NULL;

PyObject *
_PyImport_FixupExtension(char *name, char *filename)
{
    PyObject *modules, *mod, *dict, *copy;
    // 如果extensions为空,则创建PyDictObject对象赋给extensions
    if (extensions == NULL) {
        extensions = PyDict_New();
        if (extensions == NULL)
            return NULL;
    }
    // 获得进程对象的modules集合,interp->modules
    modules = PyImport_GetModuleDict();
        // 在interp->modules中找到名字为name的module
    mod = PyDict_GetItemString(modules, name);
    if (mod == NULL || !PyModule_Check(mod)) {
        PyErr_Format(PyExc_SystemError,
          "_PyImport_FixupExtension: module %.200s not loaded", name);
        return NULL;
    }
    // 抽取module中的dict
    dict = PyModule_GetDict(mod);
    if (dict == NULL)
        return NULL;
    // 对dict进行拷贝
    copy = PyDict_Copy(dict);
    if (copy == NULL)
        return NULL;
    // 将拷贝得到的心dict存储在extensions中
    PyDict_SetItemString(extensions, filename, copy);
    Py_DECREF(copy);
    return copy;
}

上面代码介绍了备份的过程,第一次会创建一个新的PyDictObject对象,这个对象将维护所有已经被Python加载的module中的PyDictObject的一个佩芬。当系统中的modules集合中的某个标准扩展module被删除后又被重新加载时,Python就不需要再次初始化这些module,只需extensions中备份的PyDictObject对象来创建一个新的module即可。

设置module搜索路径

在创建完sys module以后,会在此module中设置Python搜索一个module时的默认搜索路径集合(PySys_SetPath(Py_GetPath()))。这个路径集合就是在Python执行import xyz时将查看的路径的集合。

// sysmodule.c
void
PySys_SetPath(char *path)
{
    PyObject *v;
    if ((v = makepathobject(path, DELIM)) == NULL)
        Py_FatalError("can't create sys.path");
    if (PySys_SetObject("path", v) != 0)
        Py_FatalError("can't assign sys.path");
    Py_DECREF(v);
}

int
PySys_SetObject(char *name, PyObject *v)
{
    PyThreadState *tstate = PyThreadState_GET();
    PyObject *sd = tstate->interp->sysdict;
    if (v == NULL) {
        if (PyDict_GetItemString(sd, name) == NULL)
            return 0;
        else
            return PyDict_DelItemString(sd, name);
    }
    else
        return PyDict_SetItemString(sd, name, v);
}

makepathobject中会创建一个PyListObject对象,这个PyListObject对象中包含一组PyStringObject对象,每一个PyStringObject对象的内容就是一个module的搜索路径(也就是Python中的sys.path)。

最终,这个代表搜索路径集合的list对象会在PySys_SetObject中被插入到interp->sysdict这个PyDictObject对象(sys module中维护的那个PyDictObject对象)中。

接下来,Python会进行一些琐碎的动作,其中包含初始化Pythonimport环境,初始化Python的内建异常(其实就是调用PyType_Ready初始化各个异常类)。

创建__main__ module

_PyImportHooks_Init之后,Python会创建一个非常特殊的module__main__module

// pythonrun.c
/* Create __main__ module */
static void
initmain(void)
{
    PyObject *m, *d;
    // 创建__main__ module,并设置到interp->modules中
    m = PyImport_AddModule("__main__");
    if (m == NULL)
        Py_FatalError("can't create __main__ module");
    // 获得__main__ module中的dict
    d = PyModule_GetDict(m);
    if (PyDict_GetItemString(d, "__builtins__") == NULL) {
        // 获得interp->modules中的__builtin__ module 
        PyObject *bimod = PyImport_ImportModule("__builtin__");
        if (bimod == NULL ||
            // 将(“__builtins__”, __builtin__ module)插入到__main__ module中
            PyDict_SetItemString(d, "__builtins__", bimod) != 0)
            Py_FatalError("can't add __builtins__ to __main__");
        Py_DECREF(bimod);
    }
}

__main__ module是什么?在PyImport_AddModule时,创建了一个名为namemodule后,会在module对应的PyDictObject对象(mp_dict)设置一个名为__name__的项。__main__ module的这一项就是"__main__",作为主程序运行的Python源文件就可以被视为名为__main__module

Pythonpython abc.py运行时,Python在沿着名字空间寻找__name__时,就会最终在__main__ module中发现__name__"__main__";而如果一个py文件是以import的方式加载的,则__name__不会为"__main__"

设置site-specific的module的搜索路径

Python的第三方库通常都是由module提供,一般来说,一些规模较大的第三方库将放在%PythonHome%/lib/site-packages目录下,但是之前在初始化搜索路径集合的时候并没有将site-packages包含在内。

接下来Python通过initsite()函数将site-packages加入到搜索路径中。

// pythonrun.c
static void
initsite(void)
{
    PyObject *m, *f;
    // 获得interp->modules中的site module 
    m = PyImport_ImportModule("site");
    if (m == NULL) {
        f = PySys_GetObject("stderr");
        if (Py_VerboseFlag) {
            PyFile_WriteString(
                "'import site' failed; traceback:\n", f);
            PyErr_Print();
        }
        else {
            PyFile_WriteString(
              "'import site' failed; use -v for traceback\n", f);
            PyErr_Clear();
        }
    }
    else {
        Py_DECREF(m);
    }
}

PyImport_ImportModule函数时Pythonimport机制的核心,调用这个函数相当于Python中的import xxx,在这里进入site.py,其中会进行两个动作:

  • site-packages路径加入到sys.path中,对于不同平台有不同操作
    • win32平台:%PythonHome%/lib/site-packages
      +Unix/Linux平台:
      • %sys.prefix%/lib/python<version>/site-packages(其中%sys.prefix%Pythonsys.predix
      • %sys.prefix%/lib/site-python
      • %sys.exec_prefix%/lib/python<version>/site-packages
      • %sys.exec_prefix%/lib/site-python
  • 处理site-packages目录下的所有.pth文件中的所有路径加入到sys.path

至此,Python中绝大部分重要的初始化动作都已经完成了,下图是完成初始化后所有可以利用的资源:

完成初始化后的Python环境

0x03 激活Python虚拟机

上面部分算是完成了Python执行程序所必须的基础设施建设,但是初始化动作还没有真正完成,当Python真正进入到之前讲的字节码虚拟机后,初始化阶段才算真正完成。

PythonPy_Initialize成功完成后,最终将调用PyRun_AnyFileExFlags(fp, filename == NULL ? "<stdin>" : filename, filename != NULL, &cf)。如果以脚本方式运行Python,则filename是文件名;如果以交互方式运行Python,则filename会传入"<stdini>"。第一个参数fp指向打开的脚本或者是系统的标准输入流stdin

// pythonrun.c
/* Parse input from a file and execute it */
int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
             PyCompilerFlags *flags)
{
    if (filename == NULL)
        filename = "???";
    // 根据fp是否代表交互环境,对程序流程进行分流
    if (Py_FdIsInteractive(fp, filename)) {
        int err = PyRun_InteractiveLoopFlags(fp, filename, flags);
        if (closeit)
            fclose(fp);
        return err;
    }
    else
        return PyRun_SimpleFileExFlags(fp, filename, closeit, flags);
}

通过Py_FdIsInteractive函数判断fp是否指向标准输入流。如果是,则进入PyRun_InteractiveLoopFlags,否则进入PyRun_SimpleFileExFlags

交互式运行方式

// pythonrun.c
int
PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flags)
{
    PyObject *v;
    int ret;
    PyCompilerFlags local_flags;

    if (flags == NULL) {
        flags = &local_flags;
        local_flags.cf_flags = 0;
    }
    // 创建交互式环境提示符“>>>”
    v = PySys_GetObject("ps1");
    if (v == NULL) {
        PySys_SetObject("ps1", v = PyString_FromString(">>> "));
        Py_XDECREF(v);
    }
    // 创建交互式环境提示符“...”
    v = PySys_GetObject("ps2");
    if (v == NULL) {
        PySys_SetObject("ps2", v = PyString_FromString("... "));
        Py_XDECREF(v);
    }
    // 进入一个死循环,即交互式环境
    for (;;) {
        ret = PyRun_InteractiveOneFlags(fp, filename, flags);
        PRINT_TOTAL_REFS();
        if (ret == E_EOF)
            return 0;
        /*
        if (ret == E_NOMEM)
            return -1;
        */
    }
}

int
PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags)
{
    PyObject *m, *d, *v, *w;
    mod_ty mod;
    PyArena *arena;
    char *ps1 = "", *ps2 = "";
    int errcode = 0;

    v = PySys_GetObject("ps1");
    if (v != NULL) {
        v = PyObject_Str(v);
        if (v == NULL)
            PyErr_Clear();
        else if (PyString_Check(v))
            ps1 = PyString_AsString(v);
    }
    w = PySys_GetObject("ps2");
    if (w != NULL) {
        w = PyObject_Str(w);
        if (w == NULL)
            PyErr_Clear();
        else if (PyString_Check(w))
            ps2 = PyString_AsString(w);
    }
    // 编译用户在交互式环境下输入的Python语句
    arena = PyArena_New();
    if (arena == NULL) {
        Py_XDECREF(v);
        Py_XDECREF(w);
        return -1;
    }
    mod = PyParser_ASTFromFile(fp, filename,
                   Py_single_input, ps1, ps2,
                   flags, &errcode, arena);
    Py_XDECREF(v);
    Py_XDECREF(w);
    if (mod == NULL) {
        PyArena_Free(arena);
        if (errcode == E_EOF) {
            PyErr_Clear();
            return E_EOF;
        }
        PyErr_Print();
        return -1;
    }
    // 获得<module __main__>中维护的dict
    m = PyImport_AddModule("__main__");
    if (m == NULL) {
        PyArena_Free(arena);
        return -1;
    }
    d = PyModule_GetDict(m);
    // 执行用户输入的Python语句
    v = run_mod(mod, filename, d, d, flags, arena);
    PyArena_Free(arena);
    if (v == NULL) {
        PyErr_Print();
        return -1;
    }
    Py_DECREF(v);
    if (Py_FlushLine())
        PyErr_Clear();
    return 0;
}

调用PyParser_ASTFromFile函数,对用户在交互式环境下输入的Python语句进行编译,其结果是构造与Python语句对应的抽象语法树(AST)。

然后调用run_mode,将最终完成对用户输入的语句的执行动作。需要注意的是:在进入run_mode之前,会将__main__ module中维护的PyDictObject对象取出,传递给run_mode,这个参数很重要,它将作为Python虚拟机开始执行时当前活动的frame对象的local名字空间和global名字空间。

脚本文件运行方式

// python.h
#define Py_file_input 257

// pythonrun.c
int
PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
            PyCompilerFlags *flags)
{
    PyObject *m, *d, *v;
    const char *ext;
    // 在__main__module中设置“__file__”属性
    m = PyImport_AddModule("__main__");
    if (m == NULL)
        return -1;
    d = PyModule_GetDict(m);
    if (PyDict_GetItemString(d, "__file__") == NULL) {
        PyObject *f = PyString_FromString(filename);
        if (f == NULL)
            return -1;
        if (PyDict_SetItemString(d, "__file__", f) < 0) {
            Py_DECREF(f);
            return -1;
        }
        Py_DECREF(f);
    }
    ext = filename + strlen(filename) - 4;
    // 首先尝试去执行pyc文件
    if (maybe_pyc_file(fp, filename, ext, closeit)) {
        /* Try to run a pyc file. First, re-open in binary */
        if (closeit)
            fclose(fp);
        if ((fp = fopen(filename, "rb")) == NULL) {
            fprintf(stderr, "python: Can't reopen .pyc file\n");
            return -1;
        }
        /* Turn on optimization if a .pyo file is given */
        if (strcmp(ext, ".pyo") == 0)
            Py_OptimizeFlag = 1;
        v = run_pyc_file(fp, filename, d, d, flags);
    } else {
        // 执行py脚本文件
        v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
                      closeit, flags);
    }
        ......
    return 0;
}

PyObject *
PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,
          PyObject *locals, int closeit, PyCompilerFlags *flags)
{
    PyObject *ret;
    mod_ty mod;
    PyArena *arena = PyArena_New();
    if (arena == NULL)
        return NULL;
    // 编译
    mod = PyParser_ASTFromFile(fp, filename, start, 0, 0,
                   flags, NULL, arena);
    if (closeit)
        fclose(fp);
    if (mod == NULL) {
        PyArena_Free(arena);
        return NULL;
    }
    // 执行
    ret = run_mod(mod, filename, globals, locals, flags, arena);
    PyArena_Free(arena);
    return ret;
}

同交互式执行方式一样,脚本文件的执行流程最后也进入了run_mode,也同样将__main__ module中维护的PyDictObject对象作为localglobal名字空间传入run_mode中。

启动虚拟机

run_mode开始,Python现在只剩下最后一件需要完成的工作了,那就是启动字节码虚拟机

// pythonrun.c
static PyObject *
run_mod(mod_ty mod, const char *filename, PyObject *globals, PyObject *locals,
     PyCompilerFlags *flags, PyArena *arena)
{
    PyCodeObject *co;
    PyObject *v;
    // 基于AST编译字节码指令序列,创建PyCodeObject对象
    co = PyAST_Compile(mod, filename, flags, arena);
    if (co == NULL)
        return NULL;
    // 创建PyFrameObject对象,执行PyCodeObject对象中的字节码指令序列
    v = PyEval_EvalCode(co, globals, locals);
    Py_DECREF(co);
    return v;
}

首先是根据AST得到PyCodeObject对象,然后通过PyEval_EvalCode函数开始唤醒字节码虚拟机。

// ceval.c
PyObject *
PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)
{
    /* XXX raise SystemError if globals is NULL */
    return PyEval_EvalCodeEx(co,
              globals, locals,
              (PyObject **)NULL, 0,
              (PyObject **)NULL, 0,
              (PyObject **)NULL, 0,
              NULL);
}

PyObject *
PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
       PyObject **args, int argcount, PyObject **kws, int kwcount,
       PyObject **defs, int defcount, PyObject *closure)
{
    register PyFrameObject *f;
    register PyObject *retval = NULL;
    register PyObject **fastlocals, **freevars;
    PyThreadState *tstate = PyThreadState_GET();
    PyObject *x, *u;
    ......
    f = PyFrame_New(tstate, co, globals, locals);
    ......
    fastlocals = f->f_localsplus;
    freevars = f->f_localsplus + co->co_nlocals;
    ......
    // 真正的字节码虚拟机(伪CPU)
    retval = PyEval_EvalFrameEx(f,0);
    return retval;
}

从操作系统为Python创建进程开始,到Python虚拟机被唤醒,再到执行引擎循环执行字节码,这个过程已经很清晰了。

名字空间

在创建PyFrameObject对象时,设置的3个名字空间:localglobalbuiltin

  • builtin名字空间就是初始化时创建的__builtin__ modulePython所有线程都共享同样的builtin名字空间(节省空间,加快速度)
  • global名字空间被设置为__main__ moduledict
  • local名字空间和global名字空间一样

欢迎关注微信公众号(coder0x00)或扫描下方二维码关注,我们将持续搜寻程序员必备基础技能包提供给大家。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • 模块和包 一 模块 1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是...
    go以恒阅读 2,263评论 0 4
  • 0x01 Python虚拟机中的执行环境 Python虚拟机在执行Python代码时,是模拟操作系统执行可执行文件...
    lndyzwdxhs阅读 1,455评论 0 1
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,211评论 4 16
  • Python源码剖析笔记3-Python执行原理初探 之前写了几篇源码剖析笔记,然而慢慢觉得没有从一个宏观的角度理...
    __七把刀__阅读 73,178评论 3 30
  • 第一章初见1 “曦儿,今天去不去玩!我听说tfboys来这边拍戏了,顺便过几天还要开一次演唱会,说不定我们这一...
    冉凌曦阅读 259评论 0 0